diff --git a/transitclock/pom.xml b/transitclock/pom.xml index 37914354c..06457e14a 100755 --- a/transitclock/pom.xml +++ b/transitclock/pom.xml @@ -1,4 +1,5 @@ - + + 4.0.0 TheTransitClock @@ -139,9 +140,9 @@ - com.google.transit + io.mobilitydata.transit gtfs-realtime-bindings - 0.0.4 + 0.0.6-SNAPSHOT diff --git a/transitclock/src/main/java/org/transitclock/applications/Core.java b/transitclock/src/main/java/org/transitclock/applications/Core.java index 6fe291402..45a99d8b8 100755 --- a/transitclock/src/main/java/org/transitclock/applications/Core.java +++ b/transitclock/src/main/java/org/transitclock/applications/Core.java @@ -56,6 +56,7 @@ import org.transitclock.ipc.servers.CacheQueryServer; import org.transitclock.ipc.servers.CommandsServer; import org.transitclock.ipc.servers.ConfigServer; +import org.transitclock.ipc.servers.DiversionsServer; import org.transitclock.ipc.servers.HoldingTimeServer; import org.transitclock.ipc.servers.PredictionAnalysisServer; import org.transitclock.ipc.servers.PredictionsServer; @@ -409,6 +410,7 @@ public static void startRmiServers(String agencyId) { CacheQueryServer.start(agencyId); PredictionAnalysisServer.start(agencyId); HoldingTimeServer.start(agencyId); + DiversionsServer.start(agencyId); } static private void populateCaches() throws Exception diff --git a/transitclock/src/main/java/org/transitclock/applications/RmiQuery.java b/transitclock/src/main/java/org/transitclock/applications/RmiQuery.java index 7b90ffeb3..873ddae00 100644 --- a/transitclock/src/main/java/org/transitclock/applications/RmiQuery.java +++ b/transitclock/src/main/java/org/transitclock/applications/RmiQuery.java @@ -34,11 +34,13 @@ import org.transitclock.db.structs.Location; import org.transitclock.ipc.clients.CommandsInterfaceFactory; import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.clients.DiversionsInterfaceFactory; import org.transitclock.ipc.clients.PredictionsInterfaceFactory; import org.transitclock.ipc.clients.VehiclesInterfaceFactory; import org.transitclock.ipc.data.IpcActiveBlock; import org.transitclock.ipc.data.IpcBlock; import org.transitclock.ipc.data.IpcDirectionsForRoute; +import org.transitclock.ipc.data.IpcDiversions; import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; import org.transitclock.ipc.data.IpcRoute; import org.transitclock.ipc.data.IpcRouteSummary; @@ -48,6 +50,7 @@ import org.transitclock.ipc.data.IpcVehicleComplete; import org.transitclock.ipc.interfaces.CommandsInterface; import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.interfaces.DiversionsInterface; import org.transitclock.ipc.interfaces.PredictionsInterface; import org.transitclock.ipc.interfaces.VehiclesInterface; import org.transitclock.ipc.interfaces.PredictionsInterface.RouteStop; @@ -79,7 +82,7 @@ public class RmiQuery { private static enum Command {NOT_SPECIFIED, GET_PREDICTIONS, GET_VEHICLES, GET_ROUTE_CONFIG, GET_CONFIG, - GET_ACTIVE_BLOCKS, RESET_VEHICLE}; + GET_ACTIVE_BLOCKS, RESET_VEHICLE, GET_DETOURS}; /********************** Member Functions **************************/ @@ -197,6 +200,8 @@ else if ("activeBlocks".equals(commandStr)) command = Command.GET_ACTIVE_BLOCKS; else if ("resetVehicle".equals(commandStr)) command = Command.RESET_VEHICLE; + else if("detours".equals(commandStr)) + command = Command.GET_DETOURS; else { System.out.println("Command \"" + commandStr + "\" is not valid.\n"); displayCommandLineOptionsAndExit(options); @@ -461,13 +466,26 @@ public static void main(String[] args) { getActiveBlocks(); } else if(command == Command.RESET_VEHICLE) { resetVehicles(); - } + } else if(command == Command.GET_DETOURS) + getDetours(); + } catch (Exception e) { // Output stack trace as error message e.printStackTrace(); } } + private static void getDetours() throws Exception { + // TODO Auto-generated method stub + DiversionsInterface detourInterface=DiversionsInterfaceFactory.get(agencyId); + + IpcDiversions result = detourInterface.getDiversionsForTrip(tripId); + + if(result!=null) + System.out.println(result.toString()); + + } + private static void resetVehicles() throws Exception { CommandsInterface commandsInterface = CommandsInterfaceFactory.get(agencyId); diff --git a/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImpl.java b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImpl.java index 80b7e6c53..cecb4797e 100755 --- a/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImpl.java +++ b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImpl.java @@ -171,8 +171,8 @@ private static int getMaxStopsBetweenMatches() { * @param avlReport * @return */ - private boolean tooManyStopsTraversed(SpatialMatch oldMatch, - SpatialMatch newMatch, AvlReport previousAvlReport, + private boolean tooManyStopsTraversed(RouteMatch oldMatch, + RouteMatch newMatch, AvlReport previousAvlReport, AvlReport avlReport) { // If there is no old match then we are fine if (oldMatch == null) @@ -224,8 +224,8 @@ private boolean tooManyStopsTraversed(SpatialMatch oldMatch, * The new match for the vehicle. * @return */ - private boolean shouldProcessArrivedOrDepartedStops(SpatialMatch oldMatch, - SpatialMatch newMatch) { + private boolean shouldProcessArrivedOrDepartedStops(RouteMatch oldMatch, + RouteMatch newMatch) { // If there is no old match at all then we likely finally got a // AVL report after vehicle had left terminal. Still want to // determine arrival/departure times for the first stops of the @@ -239,7 +239,7 @@ private boolean shouldProcessArrivedOrDepartedStops(SpatialMatch oldMatch, // truly know what is going on it is best to not generate // arrivals/departures for between the matches. int stopsTraversed = - SpatialMatch.numberStopsBetweenMatches(oldMatch, newMatch); + RouteMatch.numberStopsBetweenMatches(oldMatch, newMatch); if (stopsTraversed > getMaxStopsBetweenMatches()) { logger.error("Attempting to traverse {} stops between oldMatch " + "and newMatch, which is more thanThere are more than " + @@ -611,7 +611,7 @@ private void estimateArrivalsDeparturesWithoutPreviousMatch( } // Couple of convenience variables - SpatialMatch newMatch = vehicleState.getMatch(); + RouteMatch newMatch = vehicleState.getMatch(); String vehicleId = vehicleState.getVehicleId(); if (newMatch.getTripIndex() == 0 && @@ -624,7 +624,7 @@ private void estimateArrivalsDeparturesWithoutPreviousMatch( int stopPathIndex = 0; // Determine departure time for first stop of trip - SpatialMatch beginningOfTrip = new SpatialMatch(0, block, + RouteMatch beginningOfTrip = new RouteMatch(0, block, tripIndex, 0, 0, 0.0, 0.0); long travelTimeFromFirstStopToMatch = TravelTimes.getInstance() .expectedTravelTimeBetweenMatches(vehicleId, avlReportTime, @@ -799,7 +799,7 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) { // If vehicle wasn't departing a stop then simply return the // previous AVL time as the beginTime. - SpatialMatch oldMatch = vehicleState.getPreviousMatch(); + RouteMatch oldMatch = vehicleState.getPreviousMatch(); VehicleAtStopInfo oldVehicleAtStopInfo = oldMatch.getAtStop(); AvlReport previousAvlReport = vehicleState.getPreviousAvlReportFromSuccessfulMatch(); @@ -813,13 +813,13 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) { // Use match right at the departed stop. This way we are including the // time it takes to get from the actual stop to the new match. - SpatialMatch matchJustAfterStop = + RouteMatch matchJustAfterStop = oldMatch.getMatchAdjustedToBeginningOfPath(); // Determine departure info for the old stop by using the current // AVL report and subtracting the expected travel time to get from // there to the new match. - SpatialMatch newMatch = vehicleState.getMatch(); + RouteMatch newMatch = vehicleState.getMatch(); int travelTimeToNewMatchMsec = TravelTimes.getInstance() .expectedTravelTimeBetweenMatches(vehicleId, previousAvlReport.getDate(), matchJustAfterStop, @@ -846,7 +846,7 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) { } else { // The oldMatch is before the stop so add the travel time from the // oldMatch to the stop to the previous AVL report time. - SpatialMatch matchJustBeforeStop = + RouteMatch matchJustBeforeStop = oldMatch.getMatchAdjustedToEndOfPath(); int travelTimeFromOldMatchToStopMsec = TravelTimes.getInstance() .expectedTravelTimeBetweenMatches(vehicleId, @@ -935,7 +935,7 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState, // If vehicle hasn't arrived at a stop then simply return the // AVL time as the endTime. - SpatialMatch newMatch = vehicleState.getMatch(); + RouteMatch newMatch = vehicleState.getMatch(); VehicleAtStopInfo newVehicleAtStopInfo = newMatch.getAtStop(); AvlReport avlReport = vehicleState.getAvlReport(); if (newVehicleAtStopInfo == null) @@ -949,14 +949,14 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState, // Use match right at the stop. This way we are including the // time it takes to get from the new match to the actual // stop and not just to some distance before the stop. - SpatialMatch matchJustBeforeStop = + RouteMatch matchJustBeforeStop = newMatch.getMatchAdjustedToEndOfPath(); // Determine arrival info for the new stop based on the // old AVL report. This will give us the proper time if // the vehicle already arrived before the current AVL // report - SpatialMatch oldMatch = vehicleState.getPreviousMatch(); + RouteMatch oldMatch = vehicleState.getPreviousMatch(); int travelTimeFromOldMatchMsec = TravelTimes.getInstance() .expectedTravelTimeBetweenMatches(vehicleId, avlReport.getDate(), oldMatch, matchJustBeforeStop); @@ -985,7 +985,7 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState, // The new match is after the stop so subtract the travel time // from the stop to the match from the AVL time to get the // arrivalTimeBasedOnNewMatch. - SpatialMatch matchJustAfterStop = + RouteMatch matchJustAfterStop = newMatch.getMatchAdjustedToBeginningOfPath(); int travelTimeFromStoptoNewMatchMsec = TravelTimes.getInstance() @@ -1077,8 +1077,8 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState, * @param newMatch * @return Number of zero travel or stop times */ - private int numberOfZeroTravelOrStopTimes(SpatialMatch oldMatch, - SpatialMatch newMatch) { + private int numberOfZeroTravelOrStopTimes(RouteMatch oldMatch, + RouteMatch newMatch) { int counter = 0; Indices indices = oldMatch.getIndices(); Indices newIndices = newMatch.getIndices(); @@ -1123,8 +1123,8 @@ private void handleIntermediateStops(VehicleState vehicleState, // Convenience variables String vehicleId = vehicleState.getVehicleId(); - SpatialMatch oldMatch = vehicleState.getPreviousMatch(); - SpatialMatch newMatch = vehicleState.getMatch(); + RouteMatch oldMatch = vehicleState.getPreviousMatch(); + RouteMatch newMatch = vehicleState.getMatch(); Date previousAvlDate = vehicleState .getPreviousAvlReportFromSuccessfulMatch().getDate(); Date avlDate = vehicleState.getAvlReport().getDate(); @@ -1173,7 +1173,7 @@ private void handleIntermediateStops(VehicleState vehicleState, newVehicleAtStopInfo.clone() : newMatch.getIndices(); // Determine time to first stop - SpatialMatch matchAtNextStop = oldMatch.getMatchAtJustBeforeNextStop(); + RouteMatch matchAtNextStop = oldMatch.getMatchAtJustBeforeNextStop(); long travelTimeToFirstStop = TravelTimes.getInstance() .expectedTravelTimeBetweenMatches(vehicleId, avlDate, oldMatch, matchAtNextStop); @@ -1250,7 +1250,7 @@ public void generate(VehicleState vehicleState) { // Return empty arrivalDepartures list return; } - SpatialMatch newMatch = vehicleState.getMatch(); + RouteMatch newMatch = vehicleState.getMatch(); if (newMatch == null) { logger.error("Vehicle was not matched when trying to process " + "arrival/departure times. {}", vehicleState); @@ -1264,7 +1264,7 @@ public void generate(VehicleState vehicleState) { // stop of the block due to not getting assignment right away or some // kind of AVL issue. For this situation still want to estimate the // arrival/departure times for the previous stops. - SpatialMatch oldMatch = vehicleState.getPreviousMatch(); + RouteMatch oldMatch = vehicleState.getPreviousMatch(); if (oldMatch == null) { logger.debug("For vehicleId={} there was no previous match " + "so seeing if can generate arrivals/departures for " + diff --git a/transitclock/src/main/java/org/transitclock/core/AvlProcessor.java b/transitclock/src/main/java/org/transitclock/core/AvlProcessor.java index 31b2a5a2b..794710391 100644 --- a/transitclock/src/main/java/org/transitclock/core/AvlProcessor.java +++ b/transitclock/src/main/java/org/transitclock/core/AvlProcessor.java @@ -441,17 +441,24 @@ public void matchNewFixForPredictableVehicle(VehicleState vehicleState) { logger.debug("Matching already predictable vehicle using new AVL " + "report. The old spatial match is {}", vehicleState); - // Find possible spatial matches - List spatialMatches = SpatialMatcher + // Find possible route matches + List spatialMatches = SpatialMatcher .getSpatialMatches(vehicleState); logger.debug("For vehicleId={} found the following {} spatial " + "matches: {}", vehicleState.getVehicleId(), spatialMatches.size(), spatialMatches); + + // Find possible diversion matches. + List divesionMatches = DiversionMatcher.getDiversionMatches(vehicleState); + logger.debug("For vehicleId={} found the following {} diversion " + + "matches: {}", vehicleState.getVehicleId(), + divesionMatches.size(), divesionMatches); + // Find best temporal match of the spatial matches TemporalMatch bestTemporalMatch = TemporalMatcher.getInstance() - .getBestTemporalMatch(vehicleState, spatialMatches); - + .getBestTemporalMatch(vehicleState, spatialMatches, divesionMatches); + // Log this as info since matching is a significant milestone logger.info("For vehicleId={} the best match is {}", vehicleState.getVehicleId(), bestTemporalMatch); @@ -521,7 +528,7 @@ public void matchNewFixForPredictableVehicle(VehicleState vehicleState) { * @param match * @return True if the match can be used when matching vehicle to a route */ - private static boolean matchOkForRouteMatching(SpatialMatch match) { + private static boolean matchOkForRouteMatching(RouteMatch match) { return match.awayFromTerminals(getTerminalDistanceForRouteMatching()); } @@ -684,7 +691,7 @@ private boolean matchVehicleToRouteAssignment(String routeId, } } - List allPotentialSpatialMatchesForRoute = new ArrayList(); + List allPotentialSpatialMatchesForRoute = new ArrayList(); // Go through each block and determine best spatial matches for (Block block : allBlocksForRoute) { @@ -718,12 +725,12 @@ private boolean matchVehicleToRouteAssignment(String routeId, block.getId(), potentialTrips); // Get the potential spatial matches - List spatialMatchesForBlock = SpatialMatcher + List spatialMatchesForBlock = SpatialMatcher .getSpatialMatches(vehicleState.getAvlReport(), block, potentialTrips, MatchingType.AUTO_ASSIGNING_MATCHING); // Add appropriate spatial matches to list - for (SpatialMatch spatialMatch : spatialMatchesForBlock) { + for (RouteMatch spatialMatch : spatialMatchesForBlock) { if (!SpatialMatcher.problemMatchDueToLackOfHeadingInfo( spatialMatch, vehicleState, MatchingType.AUTO_ASSIGNING_MATCHING) && matchOkForRouteMatching(spatialMatch)) @@ -779,7 +786,7 @@ private boolean matchVehicleToBlockAssignment(Block block, // specifying the block assignment so it should find a match even // if it pretty far off. List potentialTrips = block.getTripsCurrentlyActive(avlReport); - List spatialMatches = + List spatialMatches = SpatialMatcher.getSpatialMatches(vehicleState.getAvlReport(), block, potentialTrips, MatchingType.STANDARD_MATCHING); logger.debug("For vehicleId={} and blockId={} spatial matches={}", @@ -819,7 +826,7 @@ private boolean matchVehicleToBlockAssignment(Block block, double distanceToSegment = firstStopInTripLoc.distance(avlReport.getLocation()); - SpatialMatch beginningOfTrip = new SpatialMatch( + RouteMatch beginningOfTrip = new RouteMatch( avlReport.getTime(), block, block.getTripIndex(trip), 0, // stopPathIndex 0, // segmentIndex diff --git a/transitclock/src/main/java/org/transitclock/core/DiversionMatch.java b/transitclock/src/main/java/org/transitclock/core/DiversionMatch.java new file mode 100644 index 000000000..34fec5dc3 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/DiversionMatch.java @@ -0,0 +1,116 @@ +package org.transitclock.core; + +import org.transitclock.core.diversion.model.Diversion; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Location; + +public class DiversionMatch extends SpatialMatch { + Diversion diversion; + + Double distanceAlongDiversion; + Double distanceToDiversion; + + Location predictedLocation; + + String routeId; + String tripId; + String shapeId; + + Integer stopPathIndex; + Integer vectorIndex; + + public DiversionMatch(Diversion diversion, Integer stopPathIndex, Integer vectorIndex, Double distanceToDiversion, + Double distanceAlongDiversion, long avlTime, Block block, int tripIndex, String shapeId, String tripId, + String routeId) { + super(avlTime, block, tripIndex); + + this.routeId = routeId; + this.tripId = tripId; + this.shapeId = shapeId; + this.distanceAlongDiversion = distanceAlongDiversion; + this.distanceToDiversion = distanceToDiversion; + this.stopPathIndex = stopPathIndex; + this.vectorIndex = vectorIndex; + this.predictedLocation = computeLocation(); + this.diversion = diversion; + } + + public Double getDistanceAlongDiversion() { + return distanceAlongDiversion; + } + + public void setDistanceAlongDiversion(Double distanceAlongDiversion) { + this.distanceAlongDiversion = distanceAlongDiversion; + } + + public Double getDistanceToDiversion() { + return distanceToDiversion; + } + + public void setDistanceToDiversion(Double distanceToDiversion) { + this.distanceToDiversion = distanceToDiversion; + } + + public Diversion getDiversion() { + return diversion; + } + + public void setDiversion(Diversion diversion) { + this.diversion = diversion; + } + + public String getRouteId() { + return routeId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getTripId() { + return tripId; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public String getShapeId() { + return shapeId; + } + + public void setShapeId(String shapeId) { + this.shapeId = shapeId; + } + + public Location getPredictedLocation() { + return predictedLocation; + } + + public void setPredictedLocation(Location predictedLocation) { + this.predictedLocation = predictedLocation; + } + + public Integer getStopPathIndex() { + return stopPathIndex; + } + + public void setStopPathIndex(Integer stopPathIndex) { + this.stopPathIndex = stopPathIndex; + } + + @Override + public String toString() { + return "DiversionMatch [diversion=" + diversion + ", distanceAlongDiversion=" + distanceAlongDiversion + + ", distanceToDiversion=" + distanceToDiversion + ", predictedLocation=" + predictedLocation + + ", routeId=" + routeId + ", tripId=" + tripId + ", shapeId=" + shapeId + ", stopPathIndex=" + + stopPathIndex + ", vectorIndex=" + vectorIndex + "]"; + } + + @Override + protected Location computeLocation() { + /* TODO figure out location */ + return null; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/DiversionMatcher.java b/transitclock/src/main/java/org/transitclock/core/DiversionMatcher.java new file mode 100644 index 000000000..8884bdf3b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/DiversionMatcher.java @@ -0,0 +1,101 @@ +package org.transitclock.core; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.transitclock.applications.Core; +import org.transitclock.configData.CoreConfig; +import org.transitclock.core.diversion.cache.DiversionsCacheFactory; +import org.transitclock.core.diversion.cache.DiversionsKey; +import org.transitclock.core.diversion.cache.DiversionsList; +import org.transitclock.core.diversion.model.Diversion; +import org.transitclock.core.diversion.model.DiversionStopPath; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.VectorWithHeading; + +/** + * @author scrudden This is to find possible matches to a diversion. + * + */ + +public class DiversionMatcher { + + public static List getDiversionMatches(VehicleState vehicleState) { + AvlReport avlReport = vehicleState.getAvlReport(); + // The matches to be returned + List diversionMatches = new ArrayList(); + + String tripId = vehicleState.getTrip().getId(); + String routeId = vehicleState.getRouteId(); + + DiversionsKey key = new DiversionsKey(tripId, routeId); + + if (DiversionsCacheFactory.getInstance() != null) { + DiversionsList diversions = DiversionsCacheFactory.getInstance().getDiversions(key); + + if (diversions != null && diversions.getDiversions() != null) { + for (Diversion diversion : diversions.getDiversions()) { + /* Check if diversion applies to this trip. */ + if (diversion.getTripId().equals(vehicleState.getTrip().getId())) { + + /* + * Check if diversion is currently in place. Null start and end time means in + * place all the time. + */ + if ((diversion.getStartTime() == null && diversion.getEndTime() == null) || (diversion + .getStartTime().before(new Date(Core.getInstance().getSystemTime())) + && diversion.getEndTime().after(new Date(Core.getInstance().getSystemTime())))) { + Double minDistanceToSegment = null; + Integer minIndex = null; + Integer minVectorIndex = null; + Integer index = 0; + + for(DiversionStopPath diversionStopPath:diversion.getDiversionStopPaths()) + { + Integer vectorIndex=0; + for (VectorWithHeading vector : diversionStopPath.getVectors()) { + Double distanceToSegment = vector.distance(avlReport.getLocation()); + if (minDistanceToSegment == null || distanceToSegment < minDistanceToSegment) { + minDistanceToSegment = distanceToSegment; + minIndex = index; + minVectorIndex = vectorIndex; + } + vectorIndex++; + } + index++; + } + + Double distanceAlongDiveresion = 0.0; + ArrayList diversionStopPaths=diversion.getDiversionStopPaths(); + for(int i=0;i=diversion.getStartStopSeq() && indices.getStopPath().getGtfsStopSeq()<=diversion.getReturnStopSeq()) + return true; + } + return false; + } /** * Generates the predictions for the vehicle. * @@ -401,9 +415,16 @@ public List generate(VehicleState vehicleState) { // Continue through block until end of block or limit on how far // into the future should generate predictions reached. + + // Stop generating predictions if we reach a diversion. + DiversionsList diversions=null; + if(DiversionsCacheFactory.getInstance()!=null) + diversions = DiversionsCacheFactory.getInstance().getDiversions(new DiversionsKey(match.getTrip().getId(), match.getRoute().getId())); + while (schedBasedPreds || predictionTime < - avlTime + maxPredictionsTimeSecs.getValue() * Time.MS_PER_SEC) { + avlTime + maxPredictionsTimeSecs.getValue() * Time.MS_PER_SEC + || !isDiverted(indices, diversions)) { // Keep track of whether prediction is affected by layover // scheduled departure time since those predictions might not // be a accurate. Once a layover encountered then all subsequent @@ -565,6 +586,7 @@ public List generate(VehicleState vehicleState) { } + public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { //logger.debug("Using transiTime default algorithm for travel time prediction : " + indices + " Value: "+indices.getTravelTimeForPath()); @@ -584,7 +606,7 @@ public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleStat return prediction; } - public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match) + public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, RouteMatch match) { TravelTimes travelTimes = TravelTimes.getInstance(); return travelTimes.expectedTravelTimeFromMatchToEndOfStopPath(match); diff --git a/transitclock/src/main/java/org/transitclock/core/RealTimeSchedAdhProcessor.java b/transitclock/src/main/java/org/transitclock/core/RealTimeSchedAdhProcessor.java index 276ace35f..ee8050b53 100644 --- a/transitclock/src/main/java/org/transitclock/core/RealTimeSchedAdhProcessor.java +++ b/transitclock/src/main/java/org/transitclock/core/RealTimeSchedAdhProcessor.java @@ -134,7 +134,7 @@ public static TemporalDifference generate(VehicleState vehicleState) { // at the next stop with a scheduled time. Determine the // appropriate match to use for the upcoming stop where there is a // schedule time. - SpatialMatch matchAtStopWithScheduleTime = + RouteMatch matchAtStopWithScheduleTime = match.getMatchAtNextStopWithScheduleTime(); if (matchAtStopWithScheduleTime == null) return null; diff --git a/transitclock/src/main/java/org/transitclock/core/RouteMatch.java b/transitclock/src/main/java/org/transitclock/core/RouteMatch.java new file mode 100644 index 000000000..fdcbfe1a0 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/RouteMatch.java @@ -0,0 +1,998 @@ +/* + * This file is part of Transitime.org + * + * Transitime.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License (GPL) as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * Transitime.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Transitime.org . If not, see . + */ +package org.transitclock.core; + +import java.util.List; + +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.ScheduleTime; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.Vector; +import org.transitclock.db.structs.VectorWithHeading; +import org.transitclock.utils.Geo; +import org.transitclock.utils.Time; +import org.slf4j.Logger; + +/** + * Describes where an AVL report matches to an assignment spatially. + * + * @author SkiBu Smith + * + */ +public class RouteMatch extends SpatialMatch { + + protected final int stopPathIndex; + protected final int segmentIndex; + protected final double distanceToSegment; + protected final double distanceAlongSegment; + protected final VehicleAtStopInfo atStop; + + + private static final Logger logger = + LoggerFactory.getLogger(RouteMatch.class); + + + /********************** Member Functions **************************/ + + public RouteMatch(long avlTime, Block block, + int tripIndex, int stopPathIndex, int segmentIndex, + double distanceToSegment, double distanceAlongSegment) { + + super(avlTime, block, tripIndex); + + this.stopPathIndex = stopPathIndex; + this.segmentIndex = segmentIndex; + this.distanceToSegment = distanceToSegment; + this.distanceAlongSegment = distanceAlongSegment; + + // Determine whether at stop + this.atStop = atStop(); + + } + + /** + * based on the current trip/stop path/sgement compute the predicted + * vehicle location. + */ + protected Location computeLocation() { + Trip trip = block.getTrip(tripIndex); + if (trip != null) { + StopPath stopPath = trip.getStopPath(stopPathIndex); + if (stopPath != null) { + VectorWithHeading vector = stopPath.getSegmentVector(segmentIndex); + if (vector != null) { + return vector.locAlongVector(distanceAlongSegment); + } + } + } + return null; + } + + /** + * based on the indices and distance along segment compute the predicted + * vehicle location. + */ + public Location computeLocation(Indices i, double distanceAlongSegment) { + if (i != null) { + VectorWithHeading segment = i.getSegment(); + if (segment != null) { + return segment.locAlongVector(distanceAlongSegment); + } + } + return null; + } + + /** + * For making a copy of the SpatialMatch for the same trip pattern but for a + * new trip/block. This is useful when doing things by TripPattern, such as + * spatial matching. Can then make copies for the other trips that use that + * TripPattern. + * + * @param toCopy + * The SpatialMatch to copy (except for the trip/block info) + * @param newTrip + * The new trip to use for the copy. It is assumed to be for the + * same trip pattern. It can be for a separate block. + */ + public RouteMatch(RouteMatch toCopy, Trip newTrip) { + // Use the new block and trip index info + super(toCopy.avlTime, newTrip.getBlock(), newTrip.getBlock().getTripIndex(newTrip)); + if (toCopy.getTrip().getTripPattern() != newTrip.getTripPattern()) + logger.error("Trying to create a copy of a SpatialMatch using a " + + "new trip but they have different trip patterns. " + + "toCopy={} toCopy.tripPattern={} newTrip.tripPattern={}", + toCopy, toCopy.getTrip().getTripPattern().toShortString(), + newTrip.getTripPattern().toShortString()); + this.stopPathIndex = toCopy.stopPathIndex; + this.segmentIndex = toCopy.segmentIndex; + this.distanceToSegment = toCopy.distanceToSegment; + this.distanceAlongSegment = toCopy.distanceAlongSegment; + // Make a copy of the atStop. Can't simply use the toCopy + // atStop because it will be for the wrong tripIndex. Therefore + // need to create a new object. + if (toCopy.atStop() == null) { + this.atStop = null; + } else { + this.atStop = new VehicleAtStopInfo(newTrip.getBlock(), + this.tripIndex, + toCopy.atStop().getStopPathIndex()); + } + // recomupte predictedLocation for above reasons as well + this.predictedLocation = toCopy.computeLocation(toCopy.getIndices(), toCopy.distanceAlongSegment); + } + + /** + * Constructs a new SpatialMatch but at the new indices specified. Useful + * for create a match that is just before or after a stop, which is useful + * for determining travel time from stop to the next match. + * + * @param toCopy + * @param newIndices + * @param distanceAlongSegment + */ + public RouteMatch(RouteMatch toCopy, Indices newIndices, + double distanceAlongSegment) { + super(toCopy.avlTime, toCopy.block, newIndices.getTripIndex()); + this.stopPathIndex = newIndices.getStopPathIndex(); + this.segmentIndex = newIndices.getSegmentIndex(); + this.distanceToSegment = toCopy.distanceToSegment; + this.distanceAlongSegment = distanceAlongSegment; + this.atStop = toCopy.atStop; + this.predictedLocation = computeLocation(newIndices, distanceAlongSegment); + } + + /** + * For subclasses to create an object using this superclass. + * + * @param toCopy + */ + protected RouteMatch(RouteMatch toCopy) { + super(toCopy.getAvlTime(), toCopy.getBlock(), toCopy.getTripIndex()); + this.stopPathIndex = toCopy.stopPathIndex; + this.segmentIndex = toCopy.segmentIndex; + this.distanceToSegment = toCopy.distanceToSegment; + this.distanceAlongSegment = toCopy.distanceAlongSegment; + this.atStop = toCopy.atStop; + this.predictedLocation = toCopy.predictedLocation; + } + + /** + * Returns distance of this match from the beginning of the trip. + * + * @return + */ + public double distanceFromBeginningOfTrip() { + // Determine how far match is from terminal at beginning of trip + double distanceFromFirstTerminal = 0.0; + Trip trip = getTrip(); + for (int index=1; index stopPaths = + getTrip().getTripPattern().getStopPaths(); + StopPath stopPathWithScheduleTime = null; + int stopPathIndex = 0; + for (int i=getStopPathIndex(); i other.tripIndex) + return false; + if (tripIndex < other.tripIndex) + return true; + + // tripIndex == other.tripIndex + if (stopPathIndex > other.stopPathIndex) + return false; + if (stopPathIndex < other.stopPathIndex) + return true; + + // stopPathIndex == other.pathIndex + if (segmentIndex > other.segmentIndex) + return false; + if (segmentIndex < other.segmentIndex) + return true; + + // segmentIndex == other.segmentIndex + return distanceAlongSegment <= other.distanceAlongSegment; + } + + /** + * Returns true if this is before the other SpatialMatch passed in. + * + * @param other + * The Spatial Match to compare to + * @return true if this is before the other SpatialMatch + */ + public boolean lessThan(RouteMatch other) { + if (tripIndex > other.tripIndex) + return false; + if (tripIndex < other.tripIndex) + return true; + + // tripIndex == other.tripIndex + if (stopPathIndex > other.stopPathIndex) + return false; + if (stopPathIndex < other.stopPathIndex) + return true; + + // stopPathIndex == other.pathIndex + if (segmentIndex > other.segmentIndex) + return false; + if (segmentIndex < other.segmentIndex) + return true; + + // segmentIndex == other.segmentIndex + return distanceAlongSegment < other.distanceAlongSegment; + } + + /** + * Returns the total number of stops traversed between the two + * matches. + * + * @param match1 + * @param match2 + * @return Number of stops + */ + public static int numberStopsBetweenMatches(RouteMatch match1, + RouteMatch match2) { + // Stop index for first trip + int stopIdxInFirstTrip = match1.getStopPathIndex(); + + // Stops for intermediate trips + Block block = match2.getBlock(); + int numIntermediateTripsStops = 0; + for (int tripIndex = match1.getTripIndex(); + tripIndex < match2.getTripIndex(); + ++tripIndex) { + numIntermediateTripsStops += + block.getTrip(tripIndex).getNumberStopPaths(); + } + + // Stops for last trip + int stopIdxInLastTrip = match2.getStopPathIndex(); + + // Return total number of stops traversed between matches + int totalStopsBetweenMatches = stopIdxInLastTrip - stopIdxInFirstTrip + + numIntermediateTripsStops; + return totalStopsBetweenMatches; + } + + @Override + public String toString() { + return "SpatialMatch [" + + "avlTime=" + Time.dateTimeStrMsec(avlTime) + // + ", block=" + block.toShortString() too verbose! + + ", blockId=" + block.getId() + + ", tripIndex=" + tripIndex + + ", gtfsStopSeq=" + getStopPath().getGtfsStopSeq() + + ", stopPathIndex=" + stopPathIndex + + ", segmentIndex=" + segmentIndex + + ", isLayover=" + isLayover() + + ", distanceToSegment=" + Geo.distanceFormat(distanceToSegment) + + ", distanceAlongSegment=" + Geo.distanceFormat(distanceAlongSegment) + + ", distanceAlongStopPath=" + Geo.distanceFormat(getDistanceAlongStopPath()) + + ", atStop=" + atStop + + ", trip=" + getTrip().toShortString() + + "]"; + } + + /********************* Getter Methods *****************************/ + + /** + * Returns copy of the indices of the match as an Indices object. + * The Indices object is newly created and independent so + * it can be changed (incremented and decremented) at will. + * @return + */ + public Indices getIndices() { + return new Indices(this); + } + + public Block getBlock() { + return block; + } + + public Trip getTrip() { + return block.getTrips().get(tripIndex); + } + + public Route getRoute() { + return getTrip().getRoute(); + } + + /** + * Returns the vector for the segment for this SpatialMatch. + * @return + */ + public Vector getSegmentVector() { + int segmentIndex = block.numSegments(tripIndex, stopPathIndex) - 1; + Vector segmentVector = + block.getSegmentVector(tripIndex, stopPathIndex, segmentIndex); + return segmentVector; + } + + /** + * The index of which trip this is within the block. + * + * @return + */ + public int getTripIndex() { + return tripIndex; + } + + /** + * The index of which stop path this is within the trip. + * @return + */ + public int getStopPathIndex() { + return stopPathIndex; + } + + public StopPath getStopPath() { + return getTrip().getStopPath(stopPathIndex); + } + + public int getSegmentIndex() { + return segmentIndex; + } + + public double getDistanceToSegment() { + return distanceToSegment; + } + + public double getDistanceAlongSegment() { + return distanceAlongSegment; + } + + /** + * Can be either at stop at beginning of the stop path or at the stop at the + * end of the stop path. + * + * @return VehicleAtStopInfo object containing trip and path index of stop + * that match is nearby. Returns null if match is not near a stop. + */ + public VehicleAtStopInfo getAtStop() { + return atStop; + } + + /** + * Returns the VehicleAtStopInfo if match indicates that vehicle is at the + * stop at the end of the path. + * + * @return VehicleAtStopInfo if vehicle at stop at end of stop path. + * Otherwise null. + */ + public VehicleAtStopInfo getAtEndStop() { + if (atStop == null) + return null; + + // At a stop so see if it is the end of path stop + if (atStop.getStopPathIndex() == stopPathIndex) { + // At end of path stop so return it + return atStop; + } else { + // At a stop but it is the beginning stop so return null + return null; + } + } + + /** + * Returns the VehicleAtStopInfo if match indicates that vehicle is at the + * stop at the beginning of the path. + * + * @return VehicleAtStopInfo if vehicle at stop at beginning of stop path. + * Otherwise null. + */ + public VehicleAtStopInfo getAtBeginningStop() { + if (atStop == null) + return null; + + // At a stop so see if it is the end of path stop + if (atStop.getStopPathIndex() == stopPathIndex) { + // At end of path stop so return null + return null; + } else { + // At beginning stop so return it + return atStop; + } + } + + /** + * Returns true if vehicle is at or near a stop. Can be either at stop at + * beginning of the stop path or at the stop at the end of the stop path. + * + * @return true if vehicle is at or near a stop. + */ + public boolean isAtStop() { + return atStop != null; + } + + /** + * Returns true if this match indicates that the vehicle is at the stop + * indicated by the tripIndex and stopPathIndex parameters. + * + * @param tripIndex + * @param stopPathIndex + * @return True if at specified stop + */ + public boolean isAtStop(int tripIndex, int stopPathIndex) { + return atStop != null + && atStop.equals(atStop.getBlock().getId(), tripIndex, + stopPathIndex); + } + + /** + * Returns the epoch time of the AVL report for which this match was + * created. + * + * @return Time of the associated AVL report + */ + public long getAvlTime() { + return avlTime; + } + + public Location getLocation() { + return predictedLocation; + } +} + diff --git a/transitclock/src/main/java/org/transitclock/core/SpatialMatch.java b/transitclock/src/main/java/org/transitclock/core/SpatialMatch.java index c965d12d2..ca3e0363d 100644 --- a/transitclock/src/main/java/org/transitclock/core/SpatialMatch.java +++ b/transitclock/src/main/java/org/transitclock/core/SpatialMatch.java @@ -1,1009 +1,33 @@ -/* - * This file is part of Transitime.org - * - * Transitime.org is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License (GPL) as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * Transitime.org is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Transitime.org . If not, see . - */ package org.transitclock.core; -import java.util.List; - -import org.slf4j.LoggerFactory; -import org.transitclock.applications.Core; import org.transitclock.db.structs.Block; import org.transitclock.db.structs.Location; -import org.transitclock.db.structs.Route; -import org.transitclock.db.structs.ScheduleTime; -import org.transitclock.db.structs.StopPath; -import org.transitclock.db.structs.Trip; -import org.transitclock.db.structs.Vector; -import org.transitclock.db.structs.VectorWithHeading; -import org.transitclock.utils.Geo; -import org.transitclock.utils.Time; -import org.slf4j.Logger; - -/** - * Describes where an AVL report matches to an assignment spatially. - * - * @author SkiBu Smith - * - */ -public class SpatialMatch { +public abstract class SpatialMatch { protected final long avlTime; protected final Block block; protected final int tripIndex; - protected final int stopPathIndex; - protected final int segmentIndex; - protected final double distanceToSegment; - protected final double distanceAlongSegment; - protected final VehicleAtStopInfo atStop; - protected final Location predictedLocation; + protected Location predictedLocation; - private static final Logger logger = - LoggerFactory.getLogger(SpatialMatch.class); - - - /********************** Member Functions **************************/ - - public SpatialMatch(long avlTime, Block block, - int tripIndex, int stopPathIndex, int segmentIndex, - double distanceToSegment, double distanceAlongSegment) { + public Location getPredictedLocation() { + return predictedLocation; + } + public SpatialMatch(long avlTime, Block block, int tripIndex) { + super(); this.avlTime = avlTime; this.block = block; this.tripIndex = tripIndex; - this.stopPathIndex = stopPathIndex; - this.segmentIndex = segmentIndex; - this.distanceToSegment = distanceToSegment; - this.distanceAlongSegment = distanceAlongSegment; - - // Determine whether at stop - this.atStop = atStop(); - this.predictedLocation = computeLocation(); - } - - /** - * based on the current trip/stop path/sgement compute the predicted - * vehicle location. - */ - private Location computeLocation() { - Trip trip = block.getTrip(tripIndex); - if (trip != null) { - StopPath stopPath = trip.getStopPath(stopPathIndex); - if (stopPath != null) { - VectorWithHeading vector = stopPath.getSegmentVector(segmentIndex); - if (vector != null) { - return vector.locAlongVector(distanceAlongSegment); - } - } - } - return null; - } - - /** - * based on the indices and distance along segment compute the predicted - * vehicle location. - */ - public Location computeLocation(Indices i, double distanceAlongSegment) { - if (i != null) { - VectorWithHeading segment = i.getSegment(); - if (segment != null) { - return segment.locAlongVector(distanceAlongSegment); - } - } - return null; - } - - /** - * For making a copy of the SpatialMatch for the same trip pattern but for a - * new trip/block. This is useful when doing things by TripPattern, such as - * spatial matching. Can then make copies for the other trips that use that - * TripPattern. - * - * @param toCopy - * The SpatialMatch to copy (except for the trip/block info) - * @param newTrip - * The new trip to use for the copy. It is assumed to be for the - * same trip pattern. It can be for a separate block. - */ - public SpatialMatch(SpatialMatch toCopy, Trip newTrip) { - if (toCopy.getTrip().getTripPattern() != newTrip.getTripPattern()) - logger.error("Trying to create a copy of a SpatialMatch using a " - + "new trip but they have different trip patterns. " - + "toCopy={} toCopy.tripPattern={} newTrip.tripPattern={}", - toCopy, toCopy.getTrip().getTripPattern().toShortString(), - newTrip.getTripPattern().toShortString()); - this.avlTime = toCopy.avlTime; - - // Use the new block and trip index info - this.block = newTrip.getBlock(); - this.tripIndex = newTrip.getBlock().getTripIndex(newTrip); - - this.stopPathIndex = toCopy.stopPathIndex; - this.segmentIndex = toCopy.segmentIndex; - this.distanceToSegment = toCopy.distanceToSegment; - this.distanceAlongSegment = toCopy.distanceAlongSegment; - // Make a copy of the atStop. Can't simply use the toCopy - // atStop because it will be for the wrong tripIndex. Therefore - // need to create a new object. - if (toCopy.atStop() == null) { - this.atStop = null; - } else { - this.atStop = new VehicleAtStopInfo(newTrip.getBlock(), - this.tripIndex, - toCopy.atStop().getStopPathIndex()); - } - // recomupte predictedLocation for above reasons as well - this.predictedLocation = toCopy.computeLocation(toCopy.getIndices(), toCopy.distanceAlongSegment); - } - - /** - * Constructs a new SpatialMatch but at the new indices specified. Useful - * for create a match that is just before or after a stop, which is useful - * for determining travel time from stop to the next match. - * - * @param toCopy - * @param newIndices - * @param distanceAlongSegment - */ - public SpatialMatch(SpatialMatch toCopy, Indices newIndices, - double distanceAlongSegment) { - this.avlTime = toCopy.avlTime; - this.block = toCopy.block; - this.tripIndex = newIndices.getTripIndex(); - this.stopPathIndex = newIndices.getStopPathIndex(); - this.segmentIndex = newIndices.getSegmentIndex(); - this.distanceToSegment = toCopy.distanceToSegment; - this.distanceAlongSegment = distanceAlongSegment; - this.atStop = toCopy.atStop; - this.predictedLocation = computeLocation(newIndices, distanceAlongSegment); - } - - /** - * For subclasses to create an object using this superclass. - * - * @param toCopy - */ - protected SpatialMatch(SpatialMatch toCopy) { - this.avlTime = toCopy.avlTime; - this.block = toCopy.block; - this.tripIndex = toCopy.tripIndex; - this.stopPathIndex = toCopy.stopPathIndex; - this.segmentIndex = toCopy.segmentIndex; - this.distanceToSegment = toCopy.distanceToSegment; - this.distanceAlongSegment = toCopy.distanceAlongSegment; - this.atStop = toCopy.atStop; - this.predictedLocation = toCopy.predictedLocation; - } - - /** - * Returns distance of this match from the beginning of the trip. - * - * @return - */ - public double distanceFromBeginningOfTrip() { - // Determine how far match is from terminal at beginning of trip - double distanceFromFirstTerminal = 0.0; - Trip trip = getTrip(); - for (int index=1; index stopPaths = - getTrip().getTripPattern().getStopPaths(); - StopPath stopPathWithScheduleTime = null; - int stopPathIndex = 0; - for (int i=getStopPathIndex(); i other.tripIndex) - return false; - if (tripIndex < other.tripIndex) - return true; - - // tripIndex == other.tripIndex - if (stopPathIndex > other.stopPathIndex) - return false; - if (stopPathIndex < other.stopPathIndex) - return true; - - // stopPathIndex == other.pathIndex - if (segmentIndex > other.segmentIndex) - return false; - if (segmentIndex < other.segmentIndex) - return true; - - // segmentIndex == other.segmentIndex - return distanceAlongSegment <= other.distanceAlongSegment; - } - - /** - * Returns true if this is before the other SpatialMatch passed in. - * - * @param other - * The Spatial Match to compare to - * @return true if this is before the other SpatialMatch - */ - public boolean lessThan(SpatialMatch other) { - if (tripIndex > other.tripIndex) - return false; - if (tripIndex < other.tripIndex) - return true; - - // tripIndex == other.tripIndex - if (stopPathIndex > other.stopPathIndex) - return false; - if (stopPathIndex < other.stopPathIndex) - return true; - - // stopPathIndex == other.pathIndex - if (segmentIndex > other.segmentIndex) - return false; - if (segmentIndex < other.segmentIndex) - return true; - - // segmentIndex == other.segmentIndex - return distanceAlongSegment < other.distanceAlongSegment; - } - - /** - * Returns the total number of stops traversed between the two - * matches. - * - * @param match1 - * @param match2 - * @return Number of stops - */ - public static int numberStopsBetweenMatches(SpatialMatch match1, - SpatialMatch match2) { - // Stop index for first trip - int stopIdxInFirstTrip = match1.getStopPathIndex(); - - // Stops for intermediate trips - Block block = match2.getBlock(); - int numIntermediateTripsStops = 0; - for (int tripIndex = match1.getTripIndex(); - tripIndex < match2.getTripIndex(); - ++tripIndex) { - numIntermediateTripsStops += - block.getTrip(tripIndex).getNumberStopPaths(); - } - - // Stops for last trip - int stopIdxInLastTrip = match2.getStopPathIndex(); - - // Return total number of stops traversed between matches - int totalStopsBetweenMatches = stopIdxInLastTrip - stopIdxInFirstTrip - + numIntermediateTripsStops; - return totalStopsBetweenMatches; - } - - @Override - public String toString() { - return "SpatialMatch [" - + "avlTime=" + Time.dateTimeStrMsec(avlTime) - // + ", block=" + block.toShortString() too verbose! - + ", blockId=" + block.getId() - + ", tripIndex=" + tripIndex - + ", gtfsStopSeq=" + getStopPath().getGtfsStopSeq() - + ", stopPathIndex=" + stopPathIndex - + ", segmentIndex=" + segmentIndex - + ", isLayover=" + isLayover() - + ", distanceToSegment=" + Geo.distanceFormat(distanceToSegment) - + ", distanceAlongSegment=" + Geo.distanceFormat(distanceAlongSegment) - + ", distanceAlongStopPath=" + Geo.distanceFormat(getDistanceAlongStopPath()) - + ", atStop=" + atStop - + ", trip=" + getTrip().toShortString() - + "]"; - } - - /********************* Getter Methods *****************************/ - - /** - * Returns copy of the indices of the match as an Indices object. - * The Indices object is newly created and independent so - * it can be changed (incremented and decremented) at will. - * @return - */ - public Indices getIndices() { - return new Indices(this); + public long getAvlTime() { + return avlTime; } - public Block getBlock() { return block; } - - public Trip getTrip() { - return block.getTrips().get(tripIndex); - } - - public Route getRoute() { - return getTrip().getRoute(); - } - - /** - * Returns the vector for the segment for this SpatialMatch. - * @return - */ - public Vector getSegmentVector() { - int segmentIndex = block.numSegments(tripIndex, stopPathIndex) - 1; - Vector segmentVector = - block.getSegmentVector(tripIndex, stopPathIndex, segmentIndex); - return segmentVector; - } - - /** - * The index of which trip this is within the block. - * - * @return - */ public int getTripIndex() { return tripIndex; } + protected abstract Location computeLocation(); - /** - * The index of which stop path this is within the trip. - * @return - */ - public int getStopPathIndex() { - return stopPathIndex; - } - - public StopPath getStopPath() { - return getTrip().getStopPath(stopPathIndex); - } - - public int getSegmentIndex() { - return segmentIndex; - } - - public double getDistanceToSegment() { - return distanceToSegment; - } - - public double getDistanceAlongSegment() { - return distanceAlongSegment; - } - - /** - * Can be either at stop at beginning of the stop path or at the stop at the - * end of the stop path. - * - * @return VehicleAtStopInfo object containing trip and path index of stop - * that match is nearby. Returns null if match is not near a stop. - */ - public VehicleAtStopInfo getAtStop() { - return atStop; - } - - /** - * Returns the VehicleAtStopInfo if match indicates that vehicle is at the - * stop at the end of the path. - * - * @return VehicleAtStopInfo if vehicle at stop at end of stop path. - * Otherwise null. - */ - public VehicleAtStopInfo getAtEndStop() { - if (atStop == null) - return null; - - // At a stop so see if it is the end of path stop - if (atStop.getStopPathIndex() == stopPathIndex) { - // At end of path stop so return it - return atStop; - } else { - // At a stop but it is the beginning stop so return null - return null; - } - } - - /** - * Returns the VehicleAtStopInfo if match indicates that vehicle is at the - * stop at the beginning of the path. - * - * @return VehicleAtStopInfo if vehicle at stop at beginning of stop path. - * Otherwise null. - */ - public VehicleAtStopInfo getAtBeginningStop() { - if (atStop == null) - return null; - - // At a stop so see if it is the end of path stop - if (atStop.getStopPathIndex() == stopPathIndex) { - // At end of path stop so return null - return null; - } else { - // At beginning stop so return it - return atStop; - } - } - - /** - * Returns true if vehicle is at or near a stop. Can be either at stop at - * beginning of the stop path or at the stop at the end of the stop path. - * - * @return true if vehicle is at or near a stop. - */ - public boolean isAtStop() { - return atStop != null; - } - - /** - * Returns true if this match indicates that the vehicle is at the stop - * indicated by the tripIndex and stopPathIndex parameters. - * - * @param tripIndex - * @param stopPathIndex - * @return True if at specified stop - */ - public boolean isAtStop(int tripIndex, int stopPathIndex) { - return atStop != null - && atStop.equals(atStop.getBlock().getId(), tripIndex, - stopPathIndex); - } - - /** - * Returns the epoch time of the AVL report for which this match was - * created. - * - * @return Time of the associated AVL report - */ - public long getAvlTime() { - return avlTime; - } - - public Location getLocation() { - return predictedLocation; - } } - diff --git a/transitclock/src/main/java/org/transitclock/core/SpatialMatcher.java b/transitclock/src/main/java/org/transitclock/core/SpatialMatcher.java index 5d70b9efb..58260240c 100644 --- a/transitclock/src/main/java/org/transitclock/core/SpatialMatcher.java +++ b/transitclock/src/main/java/org/transitclock/core/SpatialMatcher.java @@ -51,7 +51,7 @@ public class SpatialMatcher { // So that know where to start searching from - private SpatialMatch startSearchSpatialMatch = null; + private RouteMatch startSearchSpatialMatch = null; // For keeping track of whether getting closer or further away private double previousDistanceToSegment = Double.MAX_VALUE; @@ -60,12 +60,12 @@ public class SpatialMatcher { // For keeping track of potential matches where heading and // distance to segment are acceptable. - private SpatialMatch previousPotentialSpatialMatch = null; + private RouteMatch previousPotentialSpatialMatch = null; // For keeping track of match with best distance. This is useful for // logging in case something goes wrong. It lets one determine if // need to make the system more lenient. - private SpatialMatch smallestDistanceSpatialMatch = null; + private RouteMatch smallestDistanceSpatialMatch = null; // For keeping track of what kind of spatial matching being done public enum MatchingType {STANDARD_MATCHING, AUTO_ASSIGNING_MATCHING}; @@ -95,7 +95,7 @@ private SpatialMatcher() { * * @param startSearchSpatialMatch */ - private void setStartOfSearch(SpatialMatch startSearchSpatialMatch) { + private void setStartOfSearch(RouteMatch startSearchSpatialMatch) { this.startSearchSpatialMatch = startSearchSpatialMatch; } @@ -114,12 +114,12 @@ private void setStartOfSearch(SpatialMatch startSearchSpatialMatch) { * @return List of potential SpatialMatches. Can be empty but will not be * null. */ - private List getSpatialMatchesForTrip(AvlReport avlReport, + private List getSpatialMatchesForTrip(AvlReport avlReport, Trip trip, MatchingType matchingType) { Block block = trip.getBlock(); // The matches to be returned - List spatialMatches = new ArrayList(); + List spatialMatches = new ArrayList(); // Start looking for matches at the beginning of the trip. Indices indices = new Indices(block, block.getTripIndex(trip), @@ -160,9 +160,9 @@ private List getSpatialMatchesForTrip(AvlReport avlReport, * @param spatialMatchesForTrip * @return */ - private static SpatialMatch getFirstNonLayoverSpatialMatch( - List spatialMatchesForTrip) { - for (SpatialMatch match : spatialMatchesForTrip) { + private static RouteMatch getFirstNonLayoverSpatialMatch( + List spatialMatchesForTrip) { + for (RouteMatch match : spatialMatchesForTrip) { if (!match.isLayover()) return match; } @@ -192,7 +192,7 @@ private static SpatialMatch getFirstNonLayoverSpatialMatch( * heading in proper direction for the match. */ public static boolean problemMatchDueToLackOfHeadingInfo( - SpatialMatch spatialMatch, + RouteMatch spatialMatch, VehicleState vehicleState, MatchingType matchingType) { // If there was no spatial match then there can't be a problem @@ -227,13 +227,13 @@ public static boolean problemMatchDueToLackOfHeadingInfo( return true; // Determine matches for the previous AvlReport - List spatialMatchesForPreviousReport = + List spatialMatchesForPreviousReport = (new SpatialMatcher()).getSpatialMatchesForTrip( previousAvlReport, trip, matchingType); // There can be multiple matches, but only look at first // non-layover ones for the previous report - SpatialMatch previousNonLayoverSpatialMatch = + RouteMatch previousNonLayoverSpatialMatch = getFirstNonLayoverSpatialMatch( spatialMatchesForPreviousReport); @@ -277,12 +277,12 @@ public static boolean problemMatchDueToLackOfHeadingInfo( * for keeping track of what kind of spatial matching being done * @return non-null possibly empty list of spatial matches */ - public static List getSpatialMatches( + public static List getSpatialMatches( AvlReport avlReport, Block block, List tripsToInvestigate, MatchingType matchingType) { - List spatialMatchesForAllTrips = - new ArrayList(); + List spatialMatchesForAllTrips = + new ArrayList(); // If no trips to investigate then done if (tripsToInvestigate == null || tripsToInvestigate.isEmpty()) @@ -300,9 +300,9 @@ public static List getSpatialMatches( // So can determine when to stop copying. Need to stop // when starting to look at another trip. String tripIdThatFoundTripPatternFor = null; - List matchListForIteration = - new ArrayList(spatialMatchesForAllTrips); - for (SpatialMatch spatialMatch : matchListForIteration) { + List matchListForIteration = + new ArrayList(spatialMatchesForAllTrips); + for (RouteMatch spatialMatch : matchListForIteration) { String spatialMatchTripPatternId = spatialMatch.getTrip() .getTripPattern().getId(); String currentTripPatternId = trip.getTripPattern().getId(); @@ -310,7 +310,7 @@ public static List getSpatialMatches( && (tripIdThatFoundTripPatternFor == null || tripIdThatFoundTripPatternFor .equals(spatialMatch.getTrip().getId()))) { foundTripPattern = true; - SpatialMatch spatialMatchCopy = new SpatialMatch( + RouteMatch spatialMatchCopy = new RouteMatch( spatialMatch, trip); spatialMatchesForAllTrips.add(spatialMatchCopy); tripIdThatFoundTripPatternFor = spatialMatch.getTrip() @@ -328,7 +328,7 @@ public static List getSpatialMatches( } else { // Haven't already examined this trip pattern for spatial // matches so do so now. - List spatialMatchesForTrip = + List spatialMatchesForTrip = (new SpatialMatcher()).getSpatialMatchesForTrip( avlReport, trip, matchingType); @@ -345,10 +345,10 @@ public static List getSpatialMatches( // will frequently be at the end of the trip, which should be considered // fine. if (!block.isNoSchedule()) { - Iterator iterator = + Iterator iterator = spatialMatchesForAllTrips.iterator(); while (iterator.hasNext()) { - SpatialMatch match = iterator.next(); + RouteMatch match = iterator.next(); if (block.nearEndOfBlock(match, CoreConfig .getDistanceFromEndOfBlockForInitialMatching())) { // The match is too close to end of block so don't use it @@ -392,18 +392,18 @@ public static List getSpatialMatches( * through all trips. * @return non-null possibly empty list of spatial matches */ - public static List getSpatialMatchesForAutoAssigning( + public static List getSpatialMatchesForAutoAssigning( AvlReport avlReport, Block block, List tripsToInvestigate) { // Get all the spatial matches - List allSpatialMatches = + List allSpatialMatches = getSpatialMatches(avlReport, block, tripsToInvestigate, MatchingType.AUTO_ASSIGNING_MATCHING); // Filter out the ones that are layovers - List spatialMatches = - new ArrayList(); - for (SpatialMatch spatialMatch : allSpatialMatches) { + List spatialMatches = + new ArrayList(); + for (RouteMatch spatialMatch : allSpatialMatches) { if (!spatialMatch.isLayover() || spatialMatchToLayoversAllowedForAutoAssignment.getValue()) spatialMatches.add(spatialMatch); } @@ -543,7 +543,7 @@ private boolean withinAllowableDistanceOfLayover(String vehicleId, */ private void processPossiblePotentialMatch(AvlReport avlReport, Indices potentialMatchIndices, - List spatialMatches, + List spatialMatches, MatchingType matchingType) { // Convenience variables VectorWithHeading segmentVector = potentialMatchIndices.getSegment(); @@ -611,7 +611,7 @@ private void processPossiblePotentialMatch(AvlReport avlReport, } // Create the SpatialMatch object for the specified indices - SpatialMatch spatialMatch = new SpatialMatch( + RouteMatch spatialMatch = new RouteMatch( avlReport.getTime(), potentialMatchIndices.getBlock(), potentialMatchIndices.getTripIndex(), @@ -730,14 +730,14 @@ && withinAllowableDistanceOfLayover(avlReport.getVehicleId(), * @return list of possible spatial matches. If no spatial matches then * returns empty list (as opposed to null) */ - public static List + public static List getSpatialMatches(VehicleState vehicleState) { // Some convenience variables TemporalMatch previousMatch = vehicleState.getMatch(); SpatialMatcher spatialMatcher = new SpatialMatcher(); // The matches to be returned - List spatialMatches = new ArrayList(); + List spatialMatches = new ArrayList(); // Don't want to waste time search forward too far. So limit distance // such that vehicle would have traveled at 30% more than the max speed @@ -848,7 +848,7 @@ && withinAllowableDistanceOfLayover(avlReport.getVehicleId(), int indexOfLastSegment = lastStopPath.getNumberSegments()-1; double segmentLength = lastStopPath.getSegmentVector(indexOfLastSegment).length(); - SpatialMatch matchAtEndOfBlock = new SpatialMatch( + RouteMatch matchAtEndOfBlock = new RouteMatch( vehicleState.getAvlReport().getTime(), block, previousMatch.getTripIndex(), diff --git a/transitclock/src/main/java/org/transitclock/core/TemporalMatch.java b/transitclock/src/main/java/org/transitclock/core/TemporalMatch.java index ce37c04e3..db9be16c7 100644 --- a/transitclock/src/main/java/org/transitclock/core/TemporalMatch.java +++ b/transitclock/src/main/java/org/transitclock/core/TemporalMatch.java @@ -31,13 +31,13 @@ * @author SkiBu Smith * */ -public class TemporalMatch extends SpatialMatch { +public class TemporalMatch extends RouteMatch { private final TemporalDifference temporalDifference; /********************** Member Functions **************************/ - public TemporalMatch(SpatialMatch spatialMatch, + public TemporalMatch(RouteMatch spatialMatch, TemporalDifference temporalDifference) { super(spatialMatch); this.temporalDifference = temporalDifference; diff --git a/transitclock/src/main/java/org/transitclock/core/TemporalMatcher.java b/transitclock/src/main/java/org/transitclock/core/TemporalMatcher.java index fc2cac910..dcd5349e6 100644 --- a/transitclock/src/main/java/org/transitclock/core/TemporalMatcher.java +++ b/transitclock/src/main/java/org/transitclock/core/TemporalMatcher.java @@ -82,7 +82,7 @@ public static TemporalMatcher getInstance() { * difference is beyond the allowable bounds. */ private static TemporalDifference determineHowFarOffScheduledTime( - String vehicleId, Date date, SpatialMatch spatialMatch, + String vehicleId, Date date, RouteMatch spatialMatch, boolean isFirstSpatialMatch) { // check to see if we are frequency based @@ -95,7 +95,7 @@ private static TemporalDifference determineHowFarOffScheduledTime( // Determine how long it should take to travel along trip to the match. // Can add this time to the trip scheduled start time to determine // when the vehicle is predicted to be at the match. - SpatialMatch beginningOfTrip = new SpatialMatch( + RouteMatch beginningOfTrip = new RouteMatch( 0, // AVL time doesn't matter spatialMatch.getBlock(), spatialMatch.getTripIndex(), @@ -177,7 +177,7 @@ private static TemporalDifference determineHowFarOffScheduledTime( * @return */ private TemporalDifference temporalDifferenceForSpecialLayover(VehicleState vehicleState, - SpatialMatch spatialMatch, int expectedTravelTimeMsec) { + RouteMatch spatialMatch, int expectedTravelTimeMsec) { AvlReport avlReport = vehicleState.getAvlReport(); Date avlTime = avlReport.getDate(); @@ -240,7 +240,7 @@ private TemporalDifference temporalDifferenceForSpecialLayover(VehicleState vehi * @return True if current temporal match is better and should be used */ private static boolean currentMatchIsBetter( - TemporalMatch bestTemporalMatchSoFar, SpatialMatch currentSpatialMatch, + TemporalMatch bestTemporalMatchSoFar, RouteMatch currentSpatialMatch, TemporalDifference differenceFromExpectedTime) { // If there is no current match then it can't be better if (currentSpatialMatch == null || differenceFromExpectedTime == null) @@ -307,9 +307,9 @@ private static boolean currentMatchIsBetter( * @return true if it is a problematic layover match that should not be used */ private boolean isProblematicLayover(VehicleState vehicleState, - List spatialMatches, int matchIdx) { - SpatialMatch previousMatch = vehicleState.getMatch(); - SpatialMatch spatialMatch = spatialMatches.get(matchIdx); + List spatialMatches, int matchIdx) { + RouteMatch previousMatch = vehicleState.getMatch(); + RouteMatch spatialMatch = spatialMatches.get(matchIdx); // If not even a layover then false if (!previousMatch.isLayover()) @@ -355,13 +355,14 @@ private boolean isProblematicLayover(VehicleState vehicleState, * * @param vehicleState * @param spatialMatches + * @param divesionMatches * @return The best temporal match for the spatial matches passed in. If no * valid temporal match found then returns null. */ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState, - List spatialMatches) { + List spatialMatches, List divesionMatches) { // Convenience variables - SpatialMatch previousMatch = vehicleState.getMatch(); + RouteMatch previousMatch = vehicleState.getMatch(); Date previousAvlTime = vehicleState.getPreviousAvlReportFromSuccessfulMatch().getDate(); AvlReport avlReport = vehicleState.getAvlReport(); @@ -378,7 +379,7 @@ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState, // Find best temporal match of the spatial matches TemporalMatch bestTemporalMatchSoFar = null; for (int matchIdx = 0; matchIdx < spatialMatches.size(); ++matchIdx) { - SpatialMatch spatialMatch = spatialMatches.get(matchIdx); + RouteMatch spatialMatch = spatialMatches.get(matchIdx); logger.debug("Examining spatial match {}", spatialMatch); // There is a complication with vehicles leaving a layover slightly @@ -522,9 +523,9 @@ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState, * should be. Returns null if no adequate temporal match. */ public TemporalMatch getBestTemporalMatchComparedToSchedule( - AvlReport avlReport, List spatialMatches) { + AvlReport avlReport, List spatialMatches) { TemporalDifference bestDifferenceFromExpectedTime = null; - SpatialMatch bestSpatialMatch = null; + RouteMatch bestSpatialMatch = null; logger.debug("getBestTemporalMatchComparedToSchedule has spatialMatches {}", spatialMatches); @@ -536,7 +537,7 @@ public TemporalMatch getBestTemporalMatchComparedToSchedule( } for (int i=0; i spatialMatchCache = - new HashMap(); + private Map spatialMatchCache = + new HashMap(); /****************************** Config params **********************/ @@ -252,7 +252,7 @@ private TemporalMatch bestNoScheduleMatch(Block block) { // is only for use with no schedule assignments. AvlReport avlReport = getAvlReport(); List potentialTrips = block.getTripsCurrentlyActive(avlReport); - List spatialMatches = SpatialMatcher + List spatialMatches = SpatialMatcher .getSpatialMatchesForAutoAssigning(getAvlReport(), block, potentialTrips); if (spatialMatches.isEmpty()) @@ -261,7 +261,7 @@ private TemporalMatch bestNoScheduleMatch(Block block) { // Determine all possible spatial matches for the previous AVL report so // that can make sure that it too matches the assignment. AvlReport previousAvlReport = getPreviousAvlReport(); - List prevSpatialMatches = SpatialMatcher + List prevSpatialMatches = SpatialMatcher .getSpatialMatchesForAutoAssigning(previousAvlReport, block, potentialTrips); if (prevSpatialMatches.isEmpty()) @@ -278,9 +278,9 @@ private TemporalMatch bestNoScheduleMatch(Block block) { // the previous AVL report. Find one where the expected travel time // closely matches the time between the AVL reports. TemporalDifference bestTemporalDifference = null; - SpatialMatch bestSpatialMatch = null; - for (SpatialMatch prevSpatialMatch : prevSpatialMatches) { - for (SpatialMatch spatialMatch : spatialMatches) { + RouteMatch bestSpatialMatch = null; + for (RouteMatch prevSpatialMatch : prevSpatialMatches) { + for (RouteMatch spatialMatch : spatialMatches) { // Determine according to the historic travel times how long // it was expected to take to travel from the previous match // to the current one. @@ -334,13 +334,13 @@ private TemporalMatch bestNoScheduleMatch(Block block) { * * @return All possible spatial matches */ - private List getSpatialMatches(AvlReport avlReport, + private List getSpatialMatches(AvlReport avlReport, Block block) { // Convenience variable String vehicleId = avlReport.getVehicleId(); // For returning results of this method - List spatialMatches = new ArrayList(); + List spatialMatches = new ArrayList(); // Determine which trips are currently active so that don't bother // looking at all trips @@ -367,7 +367,7 @@ private List getSpatialMatches(AvlReport avlReport, if (spatialMatchCache.containsKey(tripPatternId)) { // Already processed this trip pattern so use cached results. // Can be null - SpatialMatch previouslyFoundMatch = + RouteMatch previouslyFoundMatch = spatialMatchCache.get(tripPatternId); // If there actually was a successful spatial match to the @@ -376,8 +376,8 @@ private List getSpatialMatches(AvlReport avlReport, // The cached match has the wrong trip info so need // to create an equivalent match with the proper trip block // info - SpatialMatch matchWithProperBlock = - new SpatialMatch(previouslyFoundMatch, trip); + RouteMatch matchWithProperBlock = + new RouteMatch(previouslyFoundMatch, trip); // Add to list of spatial matches to return spatialMatches.add(matchWithProperBlock); @@ -408,13 +408,13 @@ private List getSpatialMatches(AvlReport avlReport, // spatial matches that are not layovers. If match is to a layover can // ignore it since layover matches are far too flexible to really be // considered a spatial match - List newSpatialMatches = SpatialMatcher + List newSpatialMatches = SpatialMatcher .getSpatialMatchesForAutoAssigning(avlReport, block, tripsNeedToInvestigate); // Add newly discovered matches to the cache and to the list of spatial // matches to be returned - for (SpatialMatch newSpatialMatch : newSpatialMatches) { + for (RouteMatch newSpatialMatch : newSpatialMatches) { logger.debug("For vehicleId={} for tripId={} with " + "tripPatternId={} found new spatial match {}.", vehicleId, newSpatialMatch.getTrip().getId(), @@ -440,7 +440,7 @@ private List getSpatialMatches(AvlReport avlReport, // for the trip pattern String tripPatternId = tripInvestigated.getTripPattern().getId(); boolean spatialMatchFound = false; - for (SpatialMatch newSpatialMatch : newSpatialMatches) { + for (RouteMatch newSpatialMatch : newSpatialMatches) { String spatialMatchTripPatternId = newSpatialMatch.getTrip().getTripPattern().getId(); if (spatialMatchTripPatternId.equals(tripPatternId)) { @@ -477,14 +477,14 @@ private List getSpatialMatches(AvlReport avlReport, * * @return list of spatial matches for the avlReport */ - private List getSpatialMatchesWithoutCache( + private List getSpatialMatchesWithoutCache( AvlReport avlReport, Block block) { // Determine which trips are currently active so that don't bother // looking at all trips List activeTrips = block.getTripsCurrentlyActive(avlReport); // Get and return the spatial matches - List spatialMatches = SpatialMatcher + List spatialMatches = SpatialMatcher .getSpatialMatchesForAutoAssigning(avlReport, block, activeTrips); return spatialMatches; @@ -512,7 +512,7 @@ private List getSpatialMatchesWithoutCache( private TemporalMatch bestTemporalMatch(AvlReport avlReport, Block block, boolean useCache) { // Determine all potential spatial matches for the block - List spatialMatches = useCache ? + List spatialMatches = useCache ? getSpatialMatches(avlReport, block) : getSpatialMatchesWithoutCache(avlReport, block); diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/TripDataHistoryCacheExpiry.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/TripDataHistoryCacheExpiry.java new file mode 100644 index 000000000..7a6aed135 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/TripDataHistoryCacheExpiry.java @@ -0,0 +1,29 @@ +package org.transitclock.core.dataCache.ehcache.scheduled; + +import java.util.concurrent.TimeUnit; + +import org.ehcache.ValueSupplier; +import org.ehcache.expiry.Duration; +import org.ehcache.expiry.Expiry; +import org.transitclock.core.dataCache.TripEvents; +import org.transitclock.core.dataCache.TripKey; + +public class TripDataHistoryCacheExpiry implements Expiry { + + @Override + public Duration getExpiryForCreation(TripKey key, TripEvents value) { + Duration duration=new Duration(21, TimeUnit.DAYS); + return duration; + } + + @Override + public Duration getExpiryForAccess(TripKey key, ValueSupplier value) { + return null; + } + + @Override + public Duration getExpiryForUpdate(TripKey key, ValueSupplier oldValue, TripEvents newValue) { + return null; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionCache.java b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionCache.java new file mode 100644 index 000000000..29c8c30ea --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionCache.java @@ -0,0 +1,8 @@ +package org.transitclock.core.diversion.cache; + +import org.transitclock.core.diversion.model.Diversion; + +public interface DiversionCache { + DiversionsList getDiversions(DiversionsKey key); + void putDiversion(Diversion diversion); +} diff --git a/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionCacheImpl.java b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionCacheImpl.java new file mode 100644 index 000000000..5f9242f19 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionCacheImpl.java @@ -0,0 +1,56 @@ +package org.transitclock.core.diversion.cache; + +import java.util.List; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.dataCache.ehcache.CacheManagerFactory; +import org.transitclock.core.diversion.model.Diversion; + +public class DiversionCacheImpl implements DiversionCache { + + final private static String cacheName = "diversionsCache"; + private static final Logger logger = LoggerFactory.getLogger(DiversionCacheImpl.class); + + private Cache cache = null; + + @Override + public DiversionsList getDiversions(DiversionsKey key) { + + return cache.get(key); + } + + public DiversionCacheImpl() { + CacheManager cm = CacheManagerFactory.getInstance(); + + cache = cm.getCache(cacheName, DiversionsKey.class, DiversionsList.class); + } + + @Override + public void putDiversion(Diversion diversion) { + DiversionsList result = cache.get(new DiversionsKey(diversion)); + if(result==null) + result = new DiversionsList(); + + if(!inList(result.getDiversions(),diversion)) + { + result.addDiversion(diversion); + + cache.put(new DiversionsKey(diversion), result); + } + } + private boolean inList(List list, Diversion diversion) + { + for(Diversion item:list) + { + if(item.equals(diversion)) + { + return true; + } + } + return false; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsCacheFactory.java b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsCacheFactory.java new file mode 100644 index 000000000..f417ae8e6 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsCacheFactory.java @@ -0,0 +1,23 @@ +package org.transitclock.core.diversion.cache; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.utils.ClassInstantiator; + +public class DiversionsCacheFactory { + private static StringConfigValue className = + new StringConfigValue("transitclock.core.cache.diversionsCache", + "org.transitclock.core.diversion.cache.DiversionCacheImpl", + "Specifies the class used to cache diversions."); + + private static DiversionCache singleton = null; + + public static DiversionCache getInstance() { + + if (singleton == null) { + singleton = ClassInstantiator.instantiate(className.getValue(), + DiversionCache.class); + } + + return singleton; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsKey.java b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsKey.java new file mode 100644 index 000000000..26c17e189 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsKey.java @@ -0,0 +1,61 @@ +package org.transitclock.core.diversion.cache; + +import java.io.Serializable; + +import org.transitclock.core.diversion.model.Diversion; + +public class DiversionsKey implements Serializable { + /** + * + */ + private static final long serialVersionUID = 5427491805221655172L; + String tripId; + + + public DiversionsKey(Diversion diversion) { + this.tripId=diversion.getTripId(); + + } + + public DiversionsKey(String tripId, String routeId) { + super(); + this.tripId = tripId; + } + + + + public String getTripId() { + return tripId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((tripId == null) ? 0 : tripId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DiversionsKey other = (DiversionsKey) obj; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + return true; + } + + + + + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsList.java b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsList.java new file mode 100644 index 000000000..7bd897322 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/diversion/cache/DiversionsList.java @@ -0,0 +1,56 @@ +package org.transitclock.core.diversion.cache; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import org.transitclock.core.diversion.model.Diversion; + +public class DiversionsList implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -6633598451497950140L; + + List diversions=new ArrayList(); + + public List getDiversions() { + return diversions; + } + + public void setEvents(List diversions) { + this.diversions = diversions; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((diversions == null) ? 0 : diversions.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DiversionsList other = (DiversionsList) obj; + if (diversions == null) { + if (other.diversions != null) + return false; + } else if (!diversions.equals(other.diversions)) + return false; + return true; + } + void addDiversion(Diversion diversion) + { + if(this.diversions==null) + diversions=new ArrayList(); + else + diversions.add(diversion); + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/diversion/model/Diversion.java b/transitclock/src/main/java/org/transitclock/core/diversion/model/Diversion.java new file mode 100644 index 000000000..aa7c3a28e --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/diversion/model/Diversion.java @@ -0,0 +1,253 @@ +package org.transitclock.core.diversion.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.persistence.Transient; + +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.VectorWithHeading; +import org.transitclock.ipc.data.IpcDiversion; +import org.transitclock.ipc.data.IpcDiversionStopPath; +import org.transitclock.ipc.data.IpcLocation; + +/** + * @author Sean Óg Crudden This is the starting point of modeling a detour. + */ +public class Diversion implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 8573881954740003230L; + + private String routeId; + + private String tripId; + + private String shapeId; + + private int startStopSeq; + + private int distanceStartAlongSegment; + + private int returnStopSeq; + + private int distanceEndAlongSegment; + + private ArrayList diversionStopPaths = new ArrayList(); + + private Date startTime; + + private Date endTime; + + public Diversion() { + super(); + // TODO Auto-generated constructor stub + } + + public Diversion(String routeId, String tripId, String shapeId, int startStopSeq, int distanceStartAlongSegment, + int returnStopSeq, int distanceEndAlongSegment, ArrayList diversionStopPaths,Date startTime, Date endTime) { + super(); + this.routeId = routeId; + this.tripId = tripId; + this.shapeId = shapeId; + this.startStopSeq = startStopSeq; + this.distanceStartAlongSegment = distanceStartAlongSegment; + this.returnStopSeq = returnStopSeq; + this.distanceEndAlongSegment = distanceEndAlongSegment; + this.diversionStopPaths = diversionStopPaths; + this.startTime = startTime; + this.endTime = endTime; + + } + + public Diversion(IpcDiversion diversion) { + // TODO Auto-generated constructor stub + this.routeId = diversion.getRouteId(); + this.tripId = diversion.getTripId(); + this.shapeId = diversion.getShapeId(); + this.startStopSeq = diversion.getStartStopSeq(); + this.returnStopSeq = diversion.getReturnStopSeq(); + this.distanceStartAlongSegment = diversion.getDistanceStartAlongSegment(); + this.distanceEndAlongSegment = diversion.getDistanceEndAlongSegment(); + this.startTime = diversion.getStartTime(); + this.endTime = diversion.getEndTime(); + copyStopPaths(diversion); + + } + + void copyStopPaths(IpcDiversion diversion) { + for(IpcDiversionStopPath ipcStopPath:diversion.getDiversionStopPaths()) + { + DiversionStopPath stopPath=new DiversionStopPath(); + stopPath.setDirectionId(ipcStopPath.getDirectionId()); + stopPath.setStopId(ipcStopPath.getStopId()); + stopPath.setStopName(ipcStopPath.getStopName()); + stopPath.setStopSequence(ipcStopPath.getStopSequence()); + if(ipcStopPath.getStopLocation()!=null) + stopPath.setStopLocation(new Location(ipcStopPath.getStopLocation().getLat(), ipcStopPath.getStopLocation().getLon())); + + for(IpcLocation point:ipcStopPath.getPath()) + { + stopPath.getPath().add(new Location(point.getLat(), point.getLon())); + } + + diversionStopPaths.add(stopPath); + } + } + + public ArrayList getDiversionStopPaths() { + return diversionStopPaths; + } + + public void setDiversionStopPaths(ArrayList diversionStopPaths) { + this.diversionStopPaths = diversionStopPaths; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public String getRouteId() { + return routeId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getTripId() { + return tripId; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public int getStartStopSeq() { + return startStopSeq; + } + + public void setStartStopSeq(int startStopSeq) { + this.startStopSeq = startStopSeq; + } + + public int getDistanceStartAlongSegment() { + return distanceStartAlongSegment; + } + + public void setDistanceStartAlongSegment(int distanceStartAlongSegment) { + this.distanceStartAlongSegment = distanceStartAlongSegment; + } + + public int getReturnStopSeq() { + return returnStopSeq; + } + + public void setReturnStopSeq(int returnStopSeq) { + this.returnStopSeq = returnStopSeq; + } + + public int getDistanceEndAlongSegment() { + return distanceEndAlongSegment; + } + + public void setDistanceEndAlongSegment(int distanceEndAlongSegment) { + this.distanceEndAlongSegment = distanceEndAlongSegment; + } + + public String getShapeId() { + return shapeId; + } + + public void setShapeId(String shapeId) { + this.shapeId = shapeId; + } + + @Override + public String toString() { + return "Diversion [routeId=" + routeId + ", tripId=" + tripId + ", shapeId=" + shapeId + ", startStopSeq=" + + startStopSeq + ", distanceStartAlongSegment=" + distanceStartAlongSegment + ", returnStopSeq=" + + returnStopSeq + ", distanceEndAlongSegment=" + distanceEndAlongSegment + ", diversionStopPaths=" + + diversionStopPaths + ", startTime=" + startTime + ", endTime=" + endTime + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + distanceEndAlongSegment; + result = prime * result + distanceStartAlongSegment; + result = prime * result + ((endTime == null) ? 0 : endTime.hashCode()); + result = prime * result + returnStopSeq; + result = prime * result + ((routeId == null) ? 0 : routeId.hashCode()); + result = prime * result + ((shapeId == null) ? 0 : shapeId.hashCode()); + result = prime * result + startStopSeq; + result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); + result = prime * result + ((tripId == null) ? 0 : tripId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Diversion other = (Diversion) obj; + if (distanceEndAlongSegment != other.distanceEndAlongSegment) + return false; + if (distanceStartAlongSegment != other.distanceStartAlongSegment) + return false; + if (endTime == null) { + if (other.endTime != null) + return false; + } else if (!endTime.equals(other.endTime)) + return false; + if (returnStopSeq != other.returnStopSeq) + return false; + if (routeId == null) { + if (other.routeId != null) + return false; + } else if (!routeId.equals(other.routeId)) + return false; + if (shapeId == null) { + if (other.shapeId != null) + return false; + } else if (!shapeId.equals(other.shapeId)) + return false; + if (startStopSeq != other.startStopSeq) + return false; + if (startTime == null) { + if (other.startTime != null) + return false; + } else if (!startTime.equals(other.startTime)) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + return true; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/diversion/model/DiversionStopPath.java b/transitclock/src/main/java/org/transitclock/core/diversion/model/DiversionStopPath.java new file mode 100644 index 000000000..da55ef4b4 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/diversion/model/DiversionStopPath.java @@ -0,0 +1,100 @@ +package org.transitclock.core.diversion.model; + +import java.io.Serializable; +import java.util.ArrayList; + +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.VectorWithHeading; + +public class DiversionStopPath implements Serializable { + /** + * + */ + private static final long serialVersionUID = 1598813782991216200L; + private String stopId; + private String stopName; + private Integer stopSequence; + private Location stopLocation; + private String directionId; + private ArrayList path = new ArrayList(); + + private ArrayList vectors = null; + + public DiversionStopPath() { + super(); + // TODO Auto-generated constructor stub + } + public DiversionStopPath(String stopId, String stopName, Integer stopSequence, Location stopLocation, + String directionId, ArrayList path) { + super(); + this.stopId = stopId; + this.stopName = stopName; + this.stopSequence = stopSequence; + this.stopLocation = stopLocation; + this.directionId = directionId; + this.path = path; + } + public String getDirectionId() { + return directionId; + } + public void setDirectionId(String directionId) { + this.directionId = directionId; + } + public String getStopId() { + return stopId; + } + public void setStopId(String stopId) { + this.stopId = stopId; + } + public String getStopName() { + return stopName; + } + public void setStopName(String stopName) { + this.stopName = stopName; + } + public Integer getStopSequence() { + return stopSequence; + } + public void setStopSequence(Integer stopSequence) { + this.stopSequence = stopSequence; + } + public Location getStopLocation() { + return stopLocation; + } + public void setStopLocation(Location stopLocation) { + this.stopLocation = stopLocation; + } + public ArrayList getPath() { + return path; + } + public void setPath(ArrayList path) { + this.path = path; + } + public ArrayList getVectors() { + if(vectors==null) + initVectors(); + return vectors; + } + + private void initVectors() + { + vectors = new ArrayList(path.size() - 1); + for (int segmentIndex = 0; segmentIndex < path.size() - 1; ++segmentIndex) { + VectorWithHeading v = new VectorWithHeading(nullSafeLocation(path.get(segmentIndex)), + nullSafeLocation(path.get(segmentIndex + 1))); + vectors.add(v); + } + } + private Location nullSafeLocation(Location location) { + if (location == null) { + location = new Location(0.0, 0.0); + } + return location; + } + @Override + public String toString() { + return "DiversionStopPath [stopId=" + stopId + ", stopName=" + stopName + ", stopSequence=" + stopSequence + + ", stopLocation=" + stopLocation + ", directionId=" + directionId + "]"; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/PredictionComponentElementsGenerator.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/PredictionComponentElementsGenerator.java index c521bcf0a..df32d1cdc 100755 --- a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/PredictionComponentElementsGenerator.java +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/PredictionComponentElementsGenerator.java @@ -3,7 +3,7 @@ import org.transitclock.core.HeadwayDetails; import org.transitclock.core.Indices; -import org.transitclock.core.SpatialMatch; +import org.transitclock.core.RouteMatch; import org.transitclock.core.VehicleState; import org.transitclock.db.structs.AvlReport; import org.transitclock.ipc.data.IpcPrediction; @@ -14,6 +14,6 @@ public interface PredictionComponentElementsGenerator { long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState); - long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match); + long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, RouteMatch match); } \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/average/HistoricalAveragePredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/average/HistoricalAveragePredictionGeneratorImpl.java index 4a5ac82c1..1f207adf4 100755 --- a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/average/HistoricalAveragePredictionGeneratorImpl.java +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/average/HistoricalAveragePredictionGeneratorImpl.java @@ -9,7 +9,7 @@ import org.transitclock.config.IntegerConfigValue; import org.transitclock.core.Indices; import org.transitclock.core.PredictionGeneratorDefaultImpl; -import org.transitclock.core.SpatialMatch; +import org.transitclock.core.RouteMatch; import org.transitclock.core.VehicleState; import org.transitclock.core.dataCache.HistoricalAverage; import org.transitclock.core.dataCache.StopPathCacheKey; @@ -85,7 +85,7 @@ public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleSt } @Override - public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport,SpatialMatch match) { + public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport,RouteMatch match) { Indices indices = match.getIndices(); Integer time=FrequencyBasedHistoricalAverageCache.secondsFromMidnight(new Date(match.getAvlTime()),2); diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/KalmanPredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/KalmanPredictionGeneratorImpl.java index a49bb6ac7..07fdd8415 100755 --- a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/KalmanPredictionGeneratorImpl.java +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/KalmanPredictionGeneratorImpl.java @@ -12,6 +12,7 @@ import org.transitclock.config.DoubleConfigValue; import org.transitclock.config.IntegerConfigValue; import org.transitclock.core.Indices; +import org.transitclock.core.RouteMatch; import org.transitclock.core.SpatialMatch; import org.transitclock.core.TravelTimeDetails; import org.transitclock.core.VehicleState; @@ -211,7 +212,7 @@ public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleSt } @Override - public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match) { + public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, RouteMatch match) { if(useKalmanForPartialStopPaths.getValue().booleanValue()) { diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImpl.java index b9dc02d95..01bf69b50 100755 --- a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImpl.java +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImpl.java @@ -15,6 +15,7 @@ import org.transitclock.core.HeadwayDetails; import org.transitclock.core.Indices; import org.transitclock.core.PredictionGeneratorDefaultImpl; +import org.transitclock.core.RouteMatch; import org.transitclock.core.SpatialMatch; import org.transitclock.core.TemporalDifference; import org.transitclock.core.TravelTimeDetails; @@ -227,7 +228,7 @@ public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleSt } @Override - public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match) { + public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, RouteMatch match) { if(useKalmanForPartialStopPaths.getValue().booleanValue()) { diff --git a/transitclock/src/main/java/org/transitclock/db/hibernate/AnnotatedClassesList.java b/transitclock/src/main/java/org/transitclock/db/hibernate/AnnotatedClassesList.java index 691ff8ff1..7e9e7dd35 100644 --- a/transitclock/src/main/java/org/transitclock/db/hibernate/AnnotatedClassesList.java +++ b/transitclock/src/main/java/org/transitclock/db/hibernate/AnnotatedClassesList.java @@ -27,6 +27,7 @@ import org.transitclock.db.structs.ConfigRevision; import org.transitclock.db.structs.DbTest; import org.transitclock.db.structs.Departure; +import org.transitclock.db.structs.Diversion; import org.transitclock.db.structs.FareAttribute; import org.transitclock.db.structs.FareRule; import org.transitclock.db.structs.Frequency; @@ -107,6 +108,7 @@ public class AnnotatedClassesList { VehicleState.class, HoldingTime.class, Headway.class, + Diversion.class, // For website ApiKey.class, WebAgency.class, diff --git a/transitclock/src/main/java/org/transitclock/db/structs/Block.java b/transitclock/src/main/java/org/transitclock/db/structs/Block.java index f5a2a21a6..808aa19b0 100644 --- a/transitclock/src/main/java/org/transitclock/db/structs/Block.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Block.java @@ -58,7 +58,7 @@ import org.transitclock.config.StringConfigValue; import org.transitclock.configData.AgencyConfig; import org.transitclock.configData.CoreConfig; -import org.transitclock.core.SpatialMatch; +import org.transitclock.core.RouteMatch; import org.transitclock.db.hibernate.HibernateUtils; import org.transitclock.gtfs.DbConfig; import org.transitclock.logging.Markers; @@ -1280,7 +1280,7 @@ public ScheduleTime getScheduleTime(int tripIndex, int stopPathIndex) { * @param distance * @return True if within distance of end of block */ - public boolean nearEndOfBlock(SpatialMatch match, double distance) { + public boolean nearEndOfBlock(RouteMatch match, double distance) { // If not last trip of block then not considered near end // so return false. if (match.getTripIndex() != trips.size()-1) diff --git a/transitclock/src/main/java/org/transitclock/db/structs/Diversion.java b/transitclock/src/main/java/org/transitclock/db/structs/Diversion.java new file mode 100644 index 000000000..47a3860c2 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/Diversion.java @@ -0,0 +1,299 @@ +package org.transitclock.db.structs; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OrderColumn; +import javax.persistence.Table; +import javax.persistence.Transient; + +import org.hibernate.CallbackException; +import org.hibernate.Session; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.classic.Lifecycle; + +@Entity @DynamicUpdate +@Table(name="Diversions") +public class Diversion implements Serializable, Lifecycle { + /** + * + */ + private static final long serialVersionUID = 1926444520257485356L; + + @Column + private String routeId; + + @Column + private String tripId; + + @Id + private String shapeId; + + @Column + private Date startTime; + + @Column + private Date endTime; + + @Column + private int startStopSeq; + + @Column + private int distanceStartAlongSegment; + + @Column + private int returnStopSeq; + + @Column + private int distanceEndAlongSegment; + + + @OrderColumn + private ArrayList detourPath; + + + @OrderColumn + private ArrayList stopLocations; + + + @Column + private ArrayList expectedTravelTimes; + + @Transient + private List vectors = null; + + public Diversion() { + super(); + } + public Diversion(String routeId, String tripId, String shapeId, Date startTime, Date endTime, int startStopSeq, + int distanceStartAlongSegment, int returnStopSeq, int distanceEndAlongSegment, ArrayList detourPath, + ArrayList stopLocations, ArrayListexpectedTravelTimes) { + super(); + this.routeId = routeId; + this.tripId = tripId; + this.shapeId = shapeId; + this.startTime = startTime; + this.endTime = endTime; + this.startStopSeq = startStopSeq; + this.distanceStartAlongSegment = distanceStartAlongSegment; + this.returnStopSeq = returnStopSeq; + this.distanceEndAlongSegment = distanceEndAlongSegment; + this.detourPath = detourPath; + this.stopLocations = stopLocations; + this.expectedTravelTimes = expectedTravelTimes; + + vectors = new ArrayList(detourPath.size()-1); + for (int segmentIndex=0; segmentIndex(detourPath.size()-1); + for (int segmentIndex=0; segmentIndex getDetourPath() { + return detourPath; + } + + public void setDetourPath(ArrayList detourPath) { + this.detourPath = detourPath; + } + + public List getStopLocations() { + return stopLocations; + } + + public void setStopLocations(ArrayList stopLocations) { + this.stopLocations = stopLocations; + } + + + + public List getVectors() { + return vectors; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((detourPath == null) ? 0 : detourPath.hashCode()); + result = prime * result + distanceEndAlongSegment; + result = prime * result + distanceStartAlongSegment; + result = prime * result + ((endTime == null) ? 0 : endTime.hashCode()); + result = prime * result + returnStopSeq; + result = prime * result + ((routeId == null) ? 0 : routeId.hashCode()); + result = prime * result + ((shapeId == null) ? 0 : shapeId.hashCode()); + result = prime * result + startStopSeq; + result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); + result = prime * result + ((stopLocations == null) ? 0 : stopLocations.hashCode()); + result = prime * result + ((tripId == null) ? 0 : tripId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Diversion other = (Diversion) obj; + if (detourPath == null) { + if (other.detourPath != null) + return false; + } else if (!detourPath.equals(other.detourPath)) + return false; + if (distanceEndAlongSegment != other.distanceEndAlongSegment) + return false; + if (distanceStartAlongSegment != other.distanceStartAlongSegment) + return false; + if (endTime == null) { + if (other.endTime != null) + return false; + } else if (!endTime.equals(other.endTime)) + return false; + if (returnStopSeq != other.returnStopSeq) + return false; + if (routeId == null) { + if (other.routeId != null) + return false; + } else if (!routeId.equals(other.routeId)) + return false; + if (shapeId == null) { + if (other.shapeId != null) + return false; + } else if (!shapeId.equals(other.shapeId)) + return false; + if (startStopSeq != other.startStopSeq) + return false; + if (startTime == null) { + if (other.startTime != null) + return false; + } else if (!startTime.equals(other.startTime)) + return false; + if (stopLocations == null) { + if (other.stopLocations != null) + return false; + } else if (!stopLocations.equals(other.stopLocations)) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + return true; + } + @Override + public boolean onSave(Session s) throws CallbackException { + // TODO Auto-generated method stub + return false; + } + @Override + public boolean onUpdate(Session s) throws CallbackException { + // TODO Auto-generated method stub + return false; + } + @Override + public boolean onDelete(Session s) throws CallbackException { + // TODO Auto-generated method stub + return false; + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReaderBase.java b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReaderBase.java index cf86de367..4370e0e93 100644 --- a/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReaderBase.java +++ b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReaderBase.java @@ -20,13 +20,17 @@ import java.io.InputStream; import java.net.URI; import java.net.URL; - - +import java.util.HashMap; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.transitclock.core.diversion.cache.DiversionsCacheFactory; +import org.transitclock.core.diversion.model.Diversion; +import org.transitclock.core.diversion.model.DiversionStopPath; import org.transitclock.db.structs.AvlReport; import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.db.structs.Location; import org.transitclock.utils.IntervalTimer; import org.transitclock.utils.MathUtils; import org.transitclock.utils.Time; @@ -35,7 +39,11 @@ import com.google.transit.realtime.GtfsRealtime.FeedEntity; import com.google.transit.realtime.GtfsRealtime.FeedMessage; import com.google.transit.realtime.GtfsRealtime.Position; +import com.google.transit.realtime.GtfsRealtime.Shape; +import com.google.transit.realtime.GtfsRealtime.ShapePoint; import com.google.transit.realtime.GtfsRealtime.TripDescriptor; +import com.google.transit.realtime.GtfsRealtime.TripProperties; +import com.google.transit.realtime.GtfsRealtime.TripUpdate; import com.google.transit.realtime.GtfsRealtime.VehicleDescriptor; import com.google.transit.realtime.GtfsRealtime.VehiclePosition; @@ -121,10 +129,59 @@ private void processMessage(FeedMessage message) { logger.info("Processing each individual AvlReport..."); IntervalTimer timer = new IntervalTimer(); + Map shapes=new HashMap(); + // For each entity/vehicle process the data int counter = 0; + + for (FeedEntity entity : message.getEntityList()) + { + if(entity.hasShape()) + { + Shape shape = entity.getShape(); + + if(!shapes.containsKey(shape.getShapeId())) + { + shapes.put(shape.getShapeId(), shape); + } + } + } for (FeedEntity entity : message.getEntityList()) { - // If no vehicles in the entity then nothing to process + // If no vehicles in the entity then nothing to process + if(entity.hasTripUpdate()) + { + TripUpdate tripUpdate = entity.getTripUpdate(); + if(tripUpdate.hasTripProperties()) + { + TripProperties tripProperties = tripUpdate.getTripProperties(); + if(tripProperties.hasShapeId()) + { + if(shapes.containsKey(tripProperties.getShapeId())) + { + Shape shape = shapes.get(tripProperties.getShapeId()); + + Diversion detour=new Diversion(); + detour.setShapeId(tripProperties.getShapeId()); + detour.setTripId(tripUpdate.getTrip().getTripId()); + detour.setRouteId(tripUpdate.getTrip().getRouteId()); + + DiversionStopPath diversionStopPath=new DiversionStopPath(); + for(ShapePoint shapePoint : shape.getShapePointList()) + { + Location location=new Location(shapePoint.getShapePtLat(), shapePoint.getShapePtLon()); + diversionStopPath.getPath().add(location); + } + detour.getDiversionStopPaths().add(diversionStopPath); + + if(DiversionsCacheFactory.getInstance()!=null) + DiversionsCacheFactory.getInstance().putDiversion(detour); + + logger.debug("Have found shape update to process.",tripUpdate.toString()); + } + } + } + } + if (!entity.hasVehicle()) continue; @@ -263,6 +320,7 @@ public void process() { // it never seemed to complete, even for just a single call to // parseFrom(). Therefore loading in entire file at once. FeedMessage feed = FeedMessage.parseFrom(codedStream); + logger.info("Parsing GTFS-realtime file into a FeedMessage took " + "{} msec", timer.elapsedMsec()); diff --git a/transitclock/src/main/java/org/transitclock/ipc/clients/DiversionsInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/DiversionsInterfaceFactory.java new file mode 100644 index 000000000..065e79861 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/DiversionsInterfaceFactory.java @@ -0,0 +1,61 @@ +/* + * This file is part of Transitime.org + * + * Transitime.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License (GPL) as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * Transitime.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Transitime.org . If not, see . + */ +package org.transitclock.ipc.clients; + +import java.util.HashMap; +import java.util.Map; + +import org.transitclock.ipc.interfaces.CacheQueryInterface; +import org.transitclock.ipc.interfaces.CommandsInterface; +import org.transitclock.ipc.interfaces.DiversionsInterface; +import org.transitclock.ipc.interfaces.HoldingTimeInterface; +import org.transitclock.ipc.rmi.ClientFactory; + +/** + * Provides a DiversionsInterface client that can be sent diversion queries. + * + * @author Sean Og Crudden + * + */ +public class DiversionsInterfaceFactory { + + // Keyed by agencyId + private static Map diversionsInterfaceMap = + new HashMap(); + + /********************** Member Functions **************************/ + + /** + * Gets the singleton instance. + * + * @param agencyId + * @return + */ + public static DiversionsInterface get(String agencyId) { + DiversionsInterface diversionInterface = + diversionsInterfaceMap.get(agencyId); + if (diversionInterface == null) { + + diversionInterface = ClientFactory.getInstance(agencyId, DiversionsInterface.class); + + diversionsInterfaceMap.put(agencyId, diversionInterface); + + } + return diversionInterface; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversion.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversion.java new file mode 100644 index 000000000..2a231b244 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversion.java @@ -0,0 +1,168 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.transitclock.core.diversion.model.Diversion; +import org.transitclock.core.diversion.model.DiversionStopPath; +import org.transitclock.db.structs.Location; + +public class IpcDiversion implements Serializable { + /** + * + */ + private static final long serialVersionUID = 2548744289428304091L; + + private String routeId; + + private String tripId; + + private String shapeId; + + private int startStopSeq; + + private int distanceStartAlongSegment; + + private int returnStopSeq; + + private int distanceEndAlongSegment; + + private ArrayList diversionStopPaths = new ArrayList(); + + private Date startTime; + + private Date endTime; + + public IpcDiversion() { + super(); + // TODO Auto-generated constructor stub + } + + public IpcDiversion(String routeId, String tripId, String shapeId, int startStopSeq, int distanceStartAlongSegment, + int returnStopSeq, int distanceEndAlongSegment, Date startTime, Date endTime) { + super(); + this.routeId = routeId; + this.tripId = tripId; + this.shapeId = shapeId; + this.startStopSeq = startStopSeq; + this.distanceStartAlongSegment = distanceStartAlongSegment; + this.returnStopSeq = returnStopSeq; + this.distanceEndAlongSegment = distanceEndAlongSegment; + + this.startTime = startTime; + this.endTime = endTime; + } + + public IpcDiversion(Diversion diversion) { + + this.routeId = diversion.getRouteId(); + this.tripId = diversion.getTripId(); + this.shapeId = diversion.getShapeId(); + this.startStopSeq = diversion.getStartStopSeq(); + + this.distanceStartAlongSegment = diversion.getDistanceStartAlongSegment(); + this.returnStopSeq = diversion.getReturnStopSeq(); + this.distanceEndAlongSegment = diversion.getDistanceEndAlongSegment(); + + this.startTime = diversion.getStartTime(); + this.endTime = diversion.getEndTime(); + + for(DiversionStopPath stopPath:diversion.getDiversionStopPaths()) + { + this.diversionStopPaths.add(new IpcDiversionStopPath(stopPath)); + } + } + + + + public String getRouteId() { + return routeId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getTripId() { + return tripId; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public String getShapeId() { + return shapeId; + } + + public void setShapeId(String shapeId) { + this.shapeId = shapeId; + } + + public int getStartStopSeq() { + return startStopSeq; + } + + public void setStartStopSeq(int startStopSeq) { + this.startStopSeq = startStopSeq; + } + + public int getDistanceStartAlongSegment() { + return distanceStartAlongSegment; + } + + public void setDistanceStartAlongSegment(int distanceStartAlongSegment) { + this.distanceStartAlongSegment = distanceStartAlongSegment; + } + + public int getReturnStopSeq() { + return returnStopSeq; + } + + public void setReturnStopSeq(int returnStopSeq) { + this.returnStopSeq = returnStopSeq; + } + + public int getDistanceEndAlongSegment() { + return distanceEndAlongSegment; + } + + public void setDistanceEndAlongSegment(int distanceEndAlongSegment) { + this.distanceEndAlongSegment = distanceEndAlongSegment; + } + + public ArrayList getDiversionStopPaths() { + return diversionStopPaths; + } + + public void setDiversionStopPaths(ArrayList diversionStopPaths) { + this.diversionStopPaths = diversionStopPaths; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + @Override + public String toString() { + return "IpcDiversion [routeId=" + routeId + ", tripId=" + tripId + ", shapeId=" + shapeId + ", startStopSeq=" + + startStopSeq + ", distanceStartAlongSegment=" + distanceStartAlongSegment + ", returnStopSeq=" + + returnStopSeq + ", distanceEndAlongSegment=" + distanceEndAlongSegment + ", diversionStopPaths=" + + diversionStopPaths + ", startTime=" + startTime + ", endTime=" + endTime + "]"; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversionStopPath.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversionStopPath.java new file mode 100644 index 000000000..056274a2a --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversionStopPath.java @@ -0,0 +1,87 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.ArrayList; +import org.transitclock.core.diversion.model.DiversionStopPath; +import org.transitclock.db.structs.Location; + +public class IpcDiversionStopPath implements Serializable{ + /** + * + */ + private static final long serialVersionUID = -5887640144411684439L; + private String stopId; + private String stopName; + private Integer stopSequence; + private Location stopLocation; + private String directionId; + private ArrayList path = new ArrayList(); + + public IpcDiversionStopPath(String stopId, String stopName, Integer stopSequence, Location stopLocation, + String directionId, ArrayList path) { + super(); + this.stopId = stopId; + this.stopName = stopName; + this.stopSequence = stopSequence; + this.stopLocation = stopLocation; + this.directionId = directionId; + this.path = path; + } + public IpcDiversionStopPath(DiversionStopPath diversionStopPath) + { + this.stopId=diversionStopPath.getStopId(); + this.stopName=diversionStopPath.getStopName(); + this.stopSequence=diversionStopPath.getStopSequence(); + this.stopLocation=diversionStopPath.getStopLocation(); + this.directionId=diversionStopPath.getDirectionId(); + copyPath(diversionStopPath); + } + private void copyPath(DiversionStopPath diversionStopPath) + { + for(Location point:diversionStopPath.getPath()) { + path.add(new IpcLocation(point)); + } + } + public String getStopId() { + return stopId; + } + public void setStopId(String stopId) { + this.stopId = stopId; + } + public String getStopName() { + return stopName; + } + public void setStopName(String stopName) { + this.stopName = stopName; + } + public Integer getStopSequence() { + return stopSequence; + } + public void setStopSequence(Integer stopSequence) { + this.stopSequence = stopSequence; + } + public Location getStopLocation() { + return stopLocation; + } + public void setStopLocation(Location stopLocation) { + this.stopLocation = stopLocation; + } + public String getDirectionId() { + return directionId; + } + public void setDirectionId(String directionId) { + this.directionId = directionId; + } + public ArrayList getPath() { + return path; + } + public void setPath(ArrayList path) { + this.path = path; + } + @Override + public String toString() { + return "IpcDiversionStopPath [stopId=" + stopId + ", stopName=" + stopName + ", stopSequence=" + stopSequence + + ", stopLocation=" + stopLocation + ", directionId=" + directionId + ", path=" + path + "]"; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversions.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversions.java new file mode 100644 index 000000000..bdacda570 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDiversions.java @@ -0,0 +1,30 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.List; + +import org.transitclock.core.diversion.cache.DiversionsList; + +public class IpcDiversions implements Serializable{ + /** + * + */ + private static final long serialVersionUID = 7884832425433350816L; + + private final List diversions; + + public IpcDiversions(List diversions) { + super(); + this.diversions = diversions; + } + + public List getDiversions() { + return diversions; + } + + @Override + public String toString() { + return "IpcDiversions [diversions=" + diversions + "]"; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcLocation.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcLocation.java new file mode 100644 index 000000000..7436fe77f --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcLocation.java @@ -0,0 +1,43 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; + +import org.transitclock.db.structs.Location; + +public class IpcLocation implements Serializable{ + + private Double lat; + + + private Double lon; + + + public Double getLat() { + return lat; + } + + + public Double getLon() { + return lon; + } + + + public IpcLocation(Double lat, Double lon) { + super(); + this.lat = lat; + this.lon = lon; + } + + public IpcLocation(Location location) + { + this.lat=location.getLat(); + this.lon=location.getLon(); + } + + + @Override + public String toString() { + return "IpcLocation [lat=" + lat + ", lon=" + lon + "]"; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicle.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicle.java index 422da6ca1..8507c9320 100644 --- a/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicle.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicle.java @@ -28,7 +28,7 @@ import org.transitclock.applications.Core; import org.transitclock.core.BlockAssignmentMethod; -import org.transitclock.core.SpatialMatch; +import org.transitclock.core.RouteMatch; import org.transitclock.core.TemporalDifference; import org.transitclock.core.VehicleState; import org.transitclock.core.dataCache.PredictionDataCache; @@ -113,7 +113,7 @@ public IpcVehicle(VehicleState vs) { // Get the match. If match is just after a stop then adjust // it to just before the stop so that can determine proper // stop ID and such. - SpatialMatch match = vs.getMatch().getMatchBeforeStopIfAtStop(); + RouteMatch match = vs.getMatch().getMatchBeforeStopIfAtStop(); // Determine if vehicle is at layover, and if so, what time it // should depart. The departure time isn't necessarily the diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleComplete.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleComplete.java index 0dfbb42f1..45dabddee 100644 --- a/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleComplete.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleComplete.java @@ -24,7 +24,7 @@ import java.util.List; import org.transitclock.core.BlockAssignmentMethod; -import org.transitclock.core.SpatialMatch; +import org.transitclock.core.RouteMatch; import org.transitclock.core.TemporalDifference; import org.transitclock.core.VehicleState; import org.transitclock.db.structs.Trip; @@ -75,7 +75,7 @@ public IpcVehicleComplete(VehicleState vs) { // Get the match. If match is just after a stop then adjust // it to just before the stop so that can determine proper // stop ID and such. - SpatialMatch match = vs.getMatch().getMatchBeforeStopIfAtStop(); + RouteMatch match = vs.getMatch().getMatchBeforeStopIfAtStop(); // If vehicle is at a stop then "next" stop will actually be // the current stop. this.distanceToNextStop = match.getDistanceRemainingInStopPath(); diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleGtfsRealtime.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleGtfsRealtime.java index e6765a818..88bac5ee8 100644 --- a/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleGtfsRealtime.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleGtfsRealtime.java @@ -22,7 +22,7 @@ import org.transitclock.applications.Core; import org.transitclock.core.BlockAssignmentMethod; -import org.transitclock.core.SpatialMatch; +import org.transitclock.core.RouteMatch; import org.transitclock.core.TemporalDifference; import org.transitclock.core.TemporalMatch; import org.transitclock.core.VehicleState; @@ -89,7 +89,7 @@ public IpcVehicleGtfsRealtime(VehicleState vs) { // stop ID and gtfs stop sequence. TemporalMatch temporalMatch = vs.getMatch(); if (temporalMatch != null) { - SpatialMatch match = vs.getMatch().getMatchBeforeStopIfAtStop(); + RouteMatch match = vs.getMatch().getMatchBeforeStopIfAtStop(); atStop = match.getAtStop() != null; // Determine stop info depending on whether it is at a stop or in diff --git a/transitclock/src/main/java/org/transitclock/ipc/interfaces/DiversionsInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/DiversionsInterface.java new file mode 100644 index 000000000..4ab56a5bd --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/DiversionsInterface.java @@ -0,0 +1,14 @@ +package org.transitclock.ipc.interfaces; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +import org.transitclock.ipc.data.IpcDiversion; +import org.transitclock.ipc.data.IpcDiversions; + +public interface DiversionsInterface extends Remote { + IpcDiversions getDiversionsForTrip(String tripId) throws RemoteException; + IpcDiversions getDiversionsForRoute(String routeId) throws RemoteException; + void addDiversion(IpcDiversion diversion) throws RemoteException; + void removeDiversion(IpcDiversion diversion) throws RemoteException; +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/servers/DiversionsServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/DiversionsServer.java new file mode 100644 index 000000000..2543bbaa5 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/DiversionsServer.java @@ -0,0 +1,85 @@ +package org.transitclock.ipc.servers; + +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.diversion.cache.DiversionsCacheFactory; +import org.transitclock.core.diversion.cache.DiversionsKey; +import org.transitclock.core.diversion.cache.DiversionsList; +import org.transitclock.core.diversion.model.Diversion; +import org.transitclock.db.structs.Trip; +import org.transitclock.ipc.data.IpcDiversion; +import org.transitclock.ipc.data.IpcDiversions; +import org.transitclock.ipc.interfaces.DiversionsInterface; +import org.transitclock.ipc.interfaces.HoldingTimeInterface; +import org.transitclock.ipc.rmi.AbstractServer; + +public class DiversionsServer extends AbstractServer implements DiversionsInterface { + private static DiversionsServer singleton; + + private static final Logger logger = LoggerFactory.getLogger(DiversionsServer.class); + + protected DiversionsServer(String agencyId) { + super(agencyId, DiversionsInterface.class.getSimpleName()); + } + + public static DiversionsServer start(String agencyId) { + if (singleton == null) { + singleton = new DiversionsServer(agencyId); + } + if (!singleton.getAgencyId().equals(agencyId)) { + logger.error( + "Tried calling DiversionsServer.start() for " + + "agencyId={} but the singleton was created for agencyId={}", + agencyId, singleton.getAgencyId()); + return null; + } + return singleton; + } + + @Override + public IpcDiversions getDiversionsForTrip(String tripId) throws RemoteException { + Core core = Core.getInstance(); + Trip trip = core.getDbConfig().getTrip(tripId); + + if (trip != null) { + DiversionsKey key = new DiversionsKey(tripId, trip.getRouteId()); + + DiversionsList diversions = DiversionsCacheFactory.getInstance().getDiversions(key); + + List ipcDiversions = new ArrayList(); + + for (Diversion diversion : diversions.getDiversions()) { + ipcDiversions.add(new IpcDiversion(diversion)); + } + + return new IpcDiversions(ipcDiversions); + } else { + return null; + } + } + + @Override + public IpcDiversions getDiversionsForRoute(String routeId) throws RemoteException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void addDiversion(IpcDiversion diversion) throws RemoteException { + + DiversionsCacheFactory.getInstance().putDiversion(new Diversion(diversion)); + + } + + @Override + public void removeDiversion(IpcDiversion diversion) throws RemoteException { + // TODO Auto-generated method stub + + } + +} diff --git a/transitclock/src/main/java/org/transitclock/statistics/Statistics.java b/transitclock/src/main/java/org/transitclock/statistics/Statistics.java index f3eb94e31..6d02b418f 100644 --- a/transitclock/src/main/java/org/transitclock/statistics/Statistics.java +++ b/transitclock/src/main/java/org/transitclock/statistics/Statistics.java @@ -351,9 +351,9 @@ public static double getSampleStandardDeviation(double[] values, double mean) { * Just for testing */ public static void main(String args[]) { - Integer a[] = {56449, 45916, 33983, 1237582, 32739}; - Statistics.filteredMean(Arrays.asList(a), 0.7); - + Integer a[] = {12, 41, 24, 5,10}; + int filteredmean = Statistics.filteredMean(Arrays.asList(a), 0); + System.out.println("Filtered mean=" + filteredmean + " mean="+mean(Arrays.asList(a))); Integer array[] = {2,4, 3};//, 4, 4, 4, 5, 5, 7, 9}; List values = Arrays.asList(array); diff --git a/transitclock/src/main/resources/ehcache.xml b/transitclock/src/main/resources/ehcache.xml index e8ca78cab..e930c648a 100644 --- a/transitclock/src/main/resources/ehcache.xml +++ b/transitclock/src/main/resources/ehcache.xml @@ -5,7 +5,21 @@ xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.1.xsd"> - + + + org.transitclock.core.diversion.model.Diversion.DiversionsKey + org.transitclock.core.diversion.cache.DiversionsList + + 1 + + + 2000 + 100 + 200 + + + org.transitclock.core.dataCache.StopPathCacheKey diff --git a/transitclockApi/pom.xml b/transitclockApi/pom.xml index 3049c8888..78dffb36e 100755 --- a/transitclockApi/pom.xml +++ b/transitclockApi/pom.xml @@ -1,4 +1,6 @@ - + 4.0.0 TheTransitClock @@ -8,10 +10,10 @@ transitclockApi - UTF-8 - 1.8 - 1.8 - + UTF-8 + 1.8 + 1.8 + @@ -32,21 +34,21 @@ org.glassfish.jersey.media jersey-media-moxy - 2.24.1 - - - org.slf4j - slf4j-api - 1.7.2 - + 2.11 org.glassfish.jersey.containers jersey-container-servlet - 2.24.1 + 2.11 + + commons-beanutils + commons-beanutils + 1.9.3 + + TheTransitClock transitclockCore @@ -58,46 +60,28 @@ TheTransitClock - transitclockBarefootClient + transitclockBarefootClient TheTransitClock - transitclockTraccarClient - + transitclockTraccarClient + - - com.google.guava - guava - 20.0 - - - org.glassfish.jersey.bundles.repackaged - jersey-guava - 2.6 - - - - - - - - - io.swagger.core.v3 - swagger-jaxrs2 - 2.0.2 - - - - - org.webjars - swagger-ui - 3.17.1 - + + + + + + + + + + io.swagger.core.v3 + swagger-jaxrs2 + 2.0.2 + diff --git a/transitclockApi/src/main/java/org/transitclock/api/ApiApplication.java b/transitclockApi/src/main/java/org/transitclock/api/ApiApplication.java index c4cd37373..26a874c88 100644 --- a/transitclockApi/src/main/java/org/transitclock/api/ApiApplication.java +++ b/transitclockApi/src/main/java/org/transitclock/api/ApiApplication.java @@ -38,5 +38,7 @@ public ApiApplication() { // Register all root-resource classes in package that handle @Path // requests packages("org.transitclock.api.rootResources"); + //packages("org.transitclock.api.data.customwriter"); + } } \ No newline at end of file diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversion.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversion.java new file mode 100644 index 000000000..0f847c644 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversion.java @@ -0,0 +1,113 @@ +package org.transitclock.api.data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcDiversion; +import org.transitclock.ipc.data.IpcDiversionStopPath; +import org.transitclock.ipc.data.IpcLocation; +@XmlRootElement +public class ApiDiversion implements Serializable { + /** + * + */ + private static final long serialVersionUID = 2524022174620441284L; + @XmlAttribute + private String routeId; + @XmlAttribute + private String tripId; + @XmlAttribute + private String shapeId; + @XmlAttribute + private int startStopSeq; + @XmlAttribute + private int distanceStartAlongSegment; + @XmlAttribute + private int returnStopSeq; + @XmlAttribute + private int distanceEndAlongSegment; + @XmlElement(name="stopPaths") + private List diversionStopPaths; + @XmlAttribute + private Date startTime; + @XmlAttribute + private Date endTime; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiDiversion() { + + } + + public ApiDiversion(IpcDiversion diversion) + { + this.startStopSeq=diversion.getStartStopSeq(); + this.returnStopSeq=diversion.getReturnStopSeq(); + this.routeId=diversion.getRouteId(); + this.shapeId=diversion.getShapeId(); + this.tripId=diversion.getTripId(); + this.distanceStartAlongSegment=diversion.getDistanceStartAlongSegment(); + this.distanceStartAlongSegment=diversion.getDistanceStartAlongSegment(); + + this.startTime=diversion.getStartTime(); + this.endTime=diversion.getEndTime(); + + this.diversionStopPaths=new ArrayList(); + for( IpcDiversionStopPath ipcStopPath:diversion.getDiversionStopPaths()) + { + this.diversionStopPaths.add(new ApiDiversionStopPath(ipcStopPath)); + } + } + + public String getRouteId() { + return routeId; + } + + public String getTripId() { + return tripId; + } + + public String getShapeId() { + return shapeId; + } + + public int getStartStopSeq() { + return startStopSeq; + } + + public int getDistanceStartAlongSegment() { + return distanceStartAlongSegment; + } + + public int getReturnStopSeq() { + return returnStopSeq; + } + + public int getDistanceEndAlongSegment() { + return distanceEndAlongSegment; + } + + public List getDiversionStopPaths() { + return diversionStopPaths; + } + + public Date getStartTime() { + return startTime; + } + + public Date getEndTime() { + return endTime; + } + + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversionStopPath.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversionStopPath.java new file mode 100644 index 000000000..e7baf2af4 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversionStopPath.java @@ -0,0 +1,84 @@ +package org.transitclock.api.data; + +import java.io.Serializable; +import java.util.ArrayList; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcDiversionStopPath; +import org.transitclock.ipc.data.IpcLocation; + +@XmlRootElement +public class ApiDiversionStopPath implements Serializable{ + /** + * + */ + private static final long serialVersionUID = 3943658440515490430L; + @XmlAttribute + private String stopId; + @XmlAttribute + private String stopName; + @XmlAttribute + private Integer stopSequence; + @XmlAttribute + private ApiLocation stopLocation; + @XmlAttribute + private String directionId; + @XmlElement(name = "path") + private ArrayList path = new ArrayList(); + + protected ApiDiversionStopPath() { + + } + + public ApiDiversionStopPath(String stopId, String stopName, Integer stopSequence, ApiLocation stopLocation, + String directionId, ArrayList path) { + super(); + this.stopId = stopId; + this.stopName = stopName; + this.stopSequence = stopSequence; + this.stopLocation = stopLocation; + this.directionId = directionId; + this.path = path; + } + public ApiDiversionStopPath(IpcDiversionStopPath diversionStopPath) + { + this.stopId=diversionStopPath.getStopId(); + this.stopName=diversionStopPath.getStopName(); + this.stopSequence=diversionStopPath.getStopSequence(); + if(diversionStopPath.getStopLocation()!=null) + this.stopLocation=new ApiLocation(diversionStopPath.getStopLocation()); + this.directionId=diversionStopPath.getDirectionId(); + for(IpcLocation point:diversionStopPath.getPath()) { + path.add(new ApiLocation(point)); + } + } + + public String getStopId() { + return stopId; + } + + public String getStopName() { + return stopName; + } + + public Integer getStopSequence() { + return stopSequence; + } + + public ApiLocation getStopLocation() { + return stopLocation; + } + + public String getDirectionId() { + return directionId; + } + + public ArrayList getPath() { + return path; + } + + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversions.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversions.java new file mode 100644 index 000000000..1f44bf98d --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDiversions.java @@ -0,0 +1,31 @@ +package org.transitclock.api.data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import org.transitclock.ipc.data.IpcDiversion; + +@XmlRootElement +public class ApiDiversions implements Serializable{ + + /** + * + */ + private static final long serialVersionUID = 8945488673196807736L; + protected ApiDiversions() { + + } + + public ApiDiversions(List ipcDiversions) { + diversions=new ArrayList(); + for(IpcDiversion ipcDiversion:ipcDiversions) + { + diversions.add(new ApiDiversion(ipcDiversion)); + } + } + @XmlElement(name="diversions") + private List diversions; +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiLocation.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiLocation.java index 4e5fc83cf..f2140af1a 100644 --- a/transitclockApi/src/main/java/org/transitclock/api/data/ApiLocation.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiLocation.java @@ -18,6 +18,7 @@ package org.transitclock.api.data; import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcLocation; /** * A simple latitude/longitude. @@ -48,4 +49,7 @@ public ApiLocation(double lat, double lon) { public ApiLocation(Location loc) { super(loc.getLat(), loc.getLon()); } + public ApiLocation(IpcLocation loc) { + super(loc.getLat(), loc.getLon()); + } } diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiTransientLocation.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTransientLocation.java index 94a066364..084a70dc0 100644 --- a/transitclockApi/src/main/java/org/transitclock/api/data/ApiTransientLocation.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTransientLocation.java @@ -17,6 +17,8 @@ package org.transitclock.api.data; +import java.io.Serializable; + import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlTransient; @@ -36,7 +38,12 @@ * */ @XmlTransient -public class ApiTransientLocation { +public class ApiTransientLocation implements Serializable{ + + /** + * + */ + private static final long serialVersionUID = 1997973510120089077L; @XmlAttribute private double lat; @@ -63,4 +70,13 @@ public ApiTransientLocation(double lat, double lon) { // Output only 5 digits past decimal point this.lon = MathUtils.round(latLon.getLon(), 5); } + + public double getLat() { + return lat; + } + + public double getLon() { + return lon; + } + } diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/customwriter/ApiDiversionsBodyWriter.java b/transitclockApi/src/main/java/org/transitclock/api/data/customwriter/ApiDiversionsBodyWriter.java new file mode 100644 index 000000000..9cb0d5de7 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/customwriter/ApiDiversionsBodyWriter.java @@ -0,0 +1,54 @@ +package org.transitclock.api.data.customwriter; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +import org.transitclock.api.data.ApiDiversions; +@Provider +@Produces(MediaType.APPLICATION_JSON) +public class ApiDiversionsBodyWriter implements MessageBodyWriter { + + public ApiDiversionsBodyWriter() { + super(); + // TODO Auto-generated constructor stub + } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + + return type == ApiDiversions.class; + } + + @Override + public long getSize(ApiDiversions t, Class type, Type genericType, Annotation[] annotations, + MediaType mediaType) { + + return 0; + } + + @Override + public void writeTo(ApiDiversions t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + Writer writer = new PrintWriter(entityStream); + writer.write(""); + writer.write(""); + writer.write("

JAX-RS Message Body Writer Example

"); + writer.write(""); + writer.write(""); + writer.flush(); + writer.close(); + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/rootResources/DiversionApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/DiversionApi.java new file mode 100644 index 000000000..9f48f5882 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/DiversionApi.java @@ -0,0 +1,162 @@ +package org.transitclock.api.rootResources; + +import java.util.ArrayList; + +import javax.ws.rs.BeanParam; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.commons.beanutils.BeanUtils; +import org.transitclock.api.data.ApiDiversion; +import org.transitclock.api.data.ApiDiversionStopPath; +import org.transitclock.api.data.ApiDiversions; +import org.transitclock.api.data.ApiLocation; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.api.utils.WebUtils; +import org.transitclock.core.diversion.model.Diversion; +import org.transitclock.core.diversion.model.DiversionStopPath; +import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcDiversion; +import org.transitclock.ipc.data.IpcDiversionStopPath; +import org.transitclock.ipc.data.IpcDiversions; +import org.transitclock.ipc.interfaces.DiversionsInterface; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +@Path("/key/{key}/agency/{agency}") +public class DiversionApi { + @Path("/command/getDiversionsByRoute") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Get list of active diversions for a route.", + description="This is to give the means of manually setting a vehicle unpredictable and unassigned so it will be reassigned quickly.", + tags= {"command","vehicle"}) + public Response getDiversionsByRouteId(@BeanParam StandardParameters stdParameters, + @Parameter(description="routeId to get diversions for.",required=true) @QueryParam("routeId") String routeId) throws WebApplicationException + { + stdParameters.validate(); + try { + DiversionsInterface diverionsInterface = stdParameters.getDiversionInterface(); + + IpcDiversions ipcDiversions = diverionsInterface.getDiversionsForRoute(routeId); + + ApiDiversions apiDiversions=new ApiDiversions(ipcDiversions.getDiversions()); + + return stdParameters.createResponse(apiDiversions); + + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + @Path("/command/getDiversionsByTrip") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Get a list of active diversions for a trip", + description="This is to give the means of manually setting a vehicle unpredictable and unassigned so it will be reassigned quickly.", + tags= {"command","vehicle"}) + public Response getDiversionsByTripId(@BeanParam StandardParameters stdParameters, + @Parameter(description="tripId to get diversions for.",required=true) @QueryParam("tripId") String tripId) throws WebApplicationException + { + stdParameters.validate(); + + try { + DiversionsInterface diverionsInterface = stdParameters.getDiversionInterface(); + + IpcDiversions ipcDiversions = diverionsInterface.getDiversionsForTrip(tripId); + + ApiDiversions apiDiversions=new ApiDiversions(ipcDiversions.getDiversions()); + + Response result = null; + try { + result = stdParameters.createResponse(apiDiversions); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result; + + + } catch (Exception e) { + // If problem getting data then return a Bad Request + e.printStackTrace(); + throw WebUtils.badRequestException(e); + } + } + @Path("/command/putDiversion") + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response addDiversion(@BeanParam StandardParameters stdParameters, ApiDiversion diversion) + { + String result = "Diversion added : " + diversion; + stdParameters.validate(); + + try { + DiversionsInterface diverionsInterface = stdParameters.getDiversionInterface(); + + Diversion detour=new Diversion(); + + copyProperties(detour,diversion); + + diverionsInterface.addDiversion(new IpcDiversion(detour)); + + }catch(Exception e) + { + + throw WebUtils.badRequestException(e); + } + + return Response.status(201).entity(result).build(); + + } + private void copyProperties(Diversion detour, ApiDiversion diversion) { + detour.setStartStopSeq(diversion.getStartStopSeq()); + detour.setReturnStopSeq(diversion.getReturnStopSeq()); + detour.setRouteId(diversion.getRouteId()); + detour.setTripId(diversion.getTripId()); + detour.setShapeId(diversion.getShapeId()); + detour.setDistanceStartAlongSegment(diversion.getDistanceStartAlongSegment()); + detour.setDistanceEndAlongSegment(diversion.getDistanceEndAlongSegment()); + detour.setStartTime(diversion.getStartTime()); + detour.setEndTime(diversion.getEndTime()); + + ArrayList stopPaths = new ArrayList(); + + for(ApiDiversionStopPath apiStopPath:diversion.getDiversionStopPaths()) + { + DiversionStopPath stopPath = new DiversionStopPath(); + + stopPath.setDirectionId(apiStopPath.getDirectionId()); + stopPath.setStopId(apiStopPath.getStopId()); + + if(apiStopPath.getStopLocation()!=null) + stopPath.setStopLocation(new Location(apiStopPath.getStopLocation().getLat(), apiStopPath.getStopLocation().getLon())); + + stopPath.setStopName(apiStopPath.getStopName()); + stopPath.setStopSequence(apiStopPath.getStopSequence()); + + ArrayList path=new ArrayList(); + + for(ApiLocation point:apiStopPath.getPath()) + { + path.add(new Location(point.getLat(), point.getLon())); + } + stopPath.setPath(path); + stopPaths.add(stopPath); + + } + + detour.setDiversionStopPaths(stopPaths); + } + + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/utils/StandardParameters.java b/transitclockApi/src/main/java/org/transitclock/api/utils/StandardParameters.java index 1a2052e8b..5ffffce5d 100644 --- a/transitclockApi/src/main/java/org/transitclock/api/utils/StandardParameters.java +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/StandardParameters.java @@ -33,6 +33,7 @@ import org.transitclock.ipc.clients.CacheQueryInterfaceFactory; import org.transitclock.ipc.clients.CommandsInterfaceFactory; import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.clients.DiversionsInterfaceFactory; import org.transitclock.ipc.clients.HoldingTimeInterfaceFactory; import org.transitclock.ipc.clients.PredictionAnalysisInterfaceFactory; import org.transitclock.ipc.clients.PredictionsInterfaceFactory; @@ -41,6 +42,7 @@ import org.transitclock.ipc.interfaces.CacheQueryInterface; import org.transitclock.ipc.interfaces.CommandsInterface; import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.interfaces.DiversionsInterface; import org.transitclock.ipc.interfaces.HoldingTimeInterface; import org.transitclock.ipc.interfaces.PredictionAnalysisInterface; import org.transitclock.ipc.interfaces.PredictionsInterface; @@ -315,6 +317,22 @@ public HoldingTimeInterface getHoldingTimeInterface() return holdingTimeInterface ; } + /** + * Gets the DiversionsInterface for the specified agencyId. If not valid + * then throws WebApplicationException. + * + * @return The PredictionAnalysisInterface + */ + public DiversionsInterface getDiversionInterface() + { + DiversionsInterface diversionsInterface = DiversionsInterfaceFactory.get(agencyId); + if (diversionsInterface == null) + throw WebUtils.badRequestException("Agency ID " + agencyId + + " is not valid"); + + return diversionsInterface ; + } + /** * Simple getter for the key *