diff --git a/src/main/java/org/opentripplanner/api/resource/AlertPatcher.java b/src/main/java/org/opentripplanner/api/resource/AlertPatcher.java index eba57ae0683..6bae3eb4d2d 100644 --- a/src/main/java/org/opentripplanner/api/resource/AlertPatcher.java +++ b/src/main/java/org/opentripplanner/api/resource/AlertPatcher.java @@ -16,6 +16,7 @@ the License, or (at your option) any later version. import static org.opentripplanner.api.resource.ServerInfo.Q; import java.util.Collection; +import java.util.List; import javax.annotation.security.RolesAllowed; import javax.ws.rs.Consumes; @@ -35,6 +36,8 @@ the License, or (at your option) any later version. import org.opentripplanner.routing.alertpatch.AlertPatch; import org.opentripplanner.routing.services.AlertPatchService; + + @Path("/patch") @XmlRootElement public class AlertPatcher { @@ -95,11 +98,12 @@ public AlertPatchCreationResponse createPatches(AlertPatchSet alertPatches) { return response; } - final AgencyAndId route = alertPatch.getRoute(); - if (route != null && route.getId().equals("")) { - response.status = "Every route patch must have a route id"; - return response; - } + final List routes = alertPatch.getRoute(); + for(AgencyAndId route : routes) + if (route != null && route.getId().equals("")) { + response.status = "Every route patch must have a route id"; + return response; + } } for (AlertPatch alertPatch : alertPatches.alertPatches) { alertPatchService.apply(alertPatch); diff --git a/src/main/java/org/opentripplanner/index/IndexGraphQLSchema.java b/src/main/java/org/opentripplanner/index/IndexGraphQLSchema.java index a1461c9216e..0fd9d865c4a 100644 --- a/src/main/java/org/opentripplanner/index/IndexGraphQLSchema.java +++ b/src/main/java/org/opentripplanner/index/IndexGraphQLSchema.java @@ -2,19 +2,10 @@ import static java.util.Collections.emptyList; +import java.util.*; + import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -184,6 +175,8 @@ public static String experimental(String message) { .build(); private final GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher; + + public GraphQLOutputType routeHeadsignMapType = new GraphQLTypeReference("RouteHeadsignMapType"); public GraphQLOutputType agencyType = new GraphQLTypeReference("Agency"); @@ -693,8 +686,15 @@ public IndexGraphQLSchema(GraphIndex index) { .build()) .field(GraphQLFieldDefinition.newFieldDefinition() .name("route") - .type(routeType) - .dataFetcher(environment -> index.routeForId.get(((AlertPatch) environment.getSource()).getRoute())) + .type(new GraphQLList(routeType)) + .dataFetcher(environment -> { + List rts = ((AlertPatch) environment.getSource()).getRoute(); + List routes = null; + routes = rts.stream() + .map(route -> index.routeForId.get(route)) + .collect(Collectors.toList()); + return routes; + }) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() .name("trip") @@ -961,6 +961,23 @@ public IndexGraphQLSchema(GraphIndex index) { .dataFetcher(environment -> ((StopCluster) environment.getSource()).children) .build()) .build(); + + routeHeadsignMapType = GraphQLObjectType.newObject() + .name("RouteHeadsignMapType") + .description("Route mapped with its most popular headsign") + .field(GraphQLFieldDefinition.newFieldDefinition() + .name("route") + .description("Route") + .type(routeType) + .dataFetcher(environment -> ((Map.Entry) environment.getSource()).getKey()) + .build()) + .field(GraphQLFieldDefinition.newFieldDefinition() + .name("headsign") + .description("The most popular headsign for a route") + .type(Scalars.GraphQLString) + .dataFetcher(environment -> ((Map.Entry) environment.getSource()).getValue()) + .build()) + .build(); stopType = GraphQLObjectType.newObject() .name("Stop") @@ -1098,6 +1115,69 @@ public IndexGraphQLSchema(GraphIndex index) { .dataFetcher(environment -> index.patternsForStop.get(environment.getSource())) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() + .name("topDestinationsForToday") + .type(new GraphQLList(routeHeadsignMapType)) + .argument(GraphQLArgument.newArgument() + .name("serviceDay") + .type(Scalars.GraphQLString) + .defaultValue(null) + .build()) + .dataFetcher(environment -> { + try { + BitSet services = index.servicesRunning( + ServiceDate.parseString(environment.getArgument("serviceDay")) + ); + + List> routeHeadsignList = new ArrayList>(); + Map routeTripCount = new HashMap(); + index.routesForStop(((Stop)environment + .getSource())) + .stream() + .forEach((route) -> { + Map tripCountMap = new HashMap(); + + if(!routeTripCount.containsValue(route)) + routeTripCount.put(route, new Long(0)); + index.patternsForStop.get((Stop)environment.getSource()) + .stream() + .filter(pattern -> index.patternsForRoute.get(route).contains(pattern)) + .forEach((pattern) -> { + long count = pattern.scheduledTimetable.tripTimes + .stream() + .filter(times -> services.get(times.serviceCode)) + .map(times -> times.trip).count(); + + tripCountMap.put(pattern, count); + routeTripCount.put(route, routeTripCount.get(route)+ count); + }); + + String headsignForRoute = tripCountMap + .entrySet() + .stream() + .max(Map.Entry.comparingByValue()) + .get() + .getKey() + .getDirection(); + + routeHeadsignList.add(new AbstractMap.SimpleEntry(route, headsignForRoute)); + }); + + if(routeHeadsignList != null) { + + routeHeadsignList.sort((Map.Entry e1, Map.Entry e2) -> + (int) (routeTripCount.get(e2.getKey()) - routeTripCount.get(e1.getKey()))); + return routeHeadsignList; + } + + + return null; + + } catch (ParseException e) { + return null; // Invalid date format + } + }) + .build()) + .field(GraphQLFieldDefinition.newFieldDefinition() .name("transfers") //TODO: add max distance as parameter? .type(new GraphQLList(stopAtDistanceType)) .dataFetcher(environment -> index.stopVertexForStop @@ -2435,16 +2515,39 @@ private Object getObject(String idString) { .name("feeds") .type(new GraphQLList(new GraphQLNonNull(Scalars.GraphQLString))) .build()) - .dataFetcher(environment -> environment.getArgument("feeds") != null - ? index.getAlerts() - .stream() - .filter(alertPatch -> - ((List) environment.getArgument("feeds")) - .contains(alertPatch.getFeedId()) - ) - .collect(Collectors.toList()) - : index.getAlerts() - ) + .argument(GraphQLArgument.newArgument() + .name("modes") + .type(Scalars.GraphQLString) + .build()) + .dataFetcher(environment -> { + List alerts = index.getAlerts(); + if(alerts != null) { + if(environment.getArgument("feeds") != null) + alerts = alerts + .stream() + .filter(alertPatch -> + ((List) environment.getArgument("feeds")) + .contains(alertPatch.getFeedId()) + ) + .collect(Collectors.toList()); + if(environment.getArgument("modes") != null) { + Set modes = new QualifiedModeSet( + environment.getArgument("modes")).qModes + .stream() + .map(qualifiedMode -> qualifiedMode.mode) + .filter(TraverseMode::isTransit) + .collect(Collectors.toSet()); + alerts = alerts + .stream() + .filter(alertPatch -> + modes.contains(GtfsLibrary.getTraverseMode( + index.routeForId.get(alertPatch.getRoute() instanceof AgencyAndId ? alertPatch.getRoute() : alertPatch.getRoute().get(0))))) + .collect(Collectors.toList()); + } + } + + return alerts; + }) .build()) .field(GraphQLFieldDefinition.newFieldDefinition() .name("serviceTimeRange") diff --git a/src/main/java/org/opentripplanner/routing/alertpatch/AlertPatch.java b/src/main/java/org/opentripplanner/routing/alertpatch/AlertPatch.java index a46314f9fad..04dcdb29e20 100644 --- a/src/main/java/org/opentripplanner/routing/alertpatch/AlertPatch.java +++ b/src/main/java/org/opentripplanner/routing/alertpatch/AlertPatch.java @@ -66,7 +66,7 @@ public class AlertPatch implements Serializable { private String agency; - private AgencyAndId route; + private List route = new LinkedList(); private AgencyAndId trip; @@ -120,7 +120,7 @@ public void apply(Graph graph) { Map agencies = graph.index.agenciesForFeedId.get(feedId); agency = this.agency != null ? agencies.get(this.agency) : null; } - Route route = this.route != null ? graph.index.routeForId.get(this.route) : null; + Route route = (this.route != null && this.route.size() != 0) ? graph.index.routeForId.get(this.route.get(this.route.size()-1)) : null; Stop stop = this.stop != null ? graph.index.stopForId.get(this.stop) : null; Trip trip = this.trip != null ? graph.index.tripForId.get(this.trip) : null; @@ -204,7 +204,7 @@ public void remove(Graph graph) { Map agencies = graph.index.agenciesForFeedId.get(feedId); agency = this.agency != null ? agencies.get(this.agency) : null; } - Route route = this.route != null ? graph.index.routeForId.get(this.route) : null; + Route route = (this.route != null && this.route.size() != 0) ? graph.index.routeForId.get(this.route.get(this.route.size()-1)) : null; Stop stop = this.stop != null ? graph.index.stopForId.get(this.stop) : null; Trip trip = this.trip != null ? graph.index.tripForId.get(this.trip) : null; @@ -279,7 +279,10 @@ public String getAgency() { } @XmlJavaTypeAdapter(AgencyAndIdAdapter.class) - public AgencyAndId getRoute() { + public List getRoute() { +// if(route.size() > 0) +// return route.get(route.size()-1); +// return null; return route; } @@ -298,7 +301,7 @@ public void setAgencyId(String agency) { } public void setRoute(AgencyAndId route) { - this.route = route; + this.route.add(route); } public void setTrip(AgencyAndId trip) { diff --git a/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java b/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java index bfa35a5b4f6..82148310381 100644 --- a/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java +++ b/src/main/java/org/opentripplanner/routing/graph/GraphIndex.java @@ -1079,8 +1079,13 @@ public List getAlerts() { public List getAlertsForRoute(Route route) { return getAlertPatchStream() - .filter(alertPatch -> alertPatch.getRoute() != null) - .filter(alertPatch -> route.getId().equals(alertPatch.getRoute())) + .filter(alertPatch -> { + for(AgencyAndId routeInAlerts: alertPatch.getRoute()) + if(routeInAlerts != null && route.getId().equals(routeInAlerts)) + return true; + + return false; + }) .collect(Collectors.toList()); } diff --git a/src/main/java/org/opentripplanner/routing/impl/AlertPatchServiceImpl.java b/src/main/java/org/opentripplanner/routing/impl/AlertPatchServiceImpl.java index 5cf4119c1e7..c4b6a75ff69 100644 --- a/src/main/java/org/opentripplanner/routing/impl/AlertPatchServiceImpl.java +++ b/src/main/java/org/opentripplanner/routing/impl/AlertPatchServiceImpl.java @@ -84,8 +84,9 @@ public synchronized void apply(AlertPatch alertPatch) { if (stop != null) { patchesByStop.put(stop, alertPatch); } - AgencyAndId route = alertPatch.getRoute(); - if (route != null) { + + List routes = alertPatch.getRoute(); + for( AgencyAndId route: routes) { patchesByRoute.put(route, alertPatch); } } @@ -128,8 +129,8 @@ private void expire(AlertPatch alertPatch) { if (stop != null) { patchesByStop.remove(stop, alertPatch); } - AgencyAndId route = alertPatch.getRoute(); - if (route != null) { + List routes = alertPatch.getRoute(); + for( AgencyAndId route: routes) { patchesByRoute.remove(route, alertPatch); } diff --git a/src/main/java/org/opentripplanner/updater/alerts/AlertsUpdateHandler.java b/src/main/java/org/opentripplanner/updater/alerts/AlertsUpdateHandler.java index 6822ce24ca6..4513f570727 100644 --- a/src/main/java/org/opentripplanner/updater/alerts/AlertsUpdateHandler.java +++ b/src/main/java/org/opentripplanner/updater/alerts/AlertsUpdateHandler.java @@ -98,6 +98,9 @@ private void handleAlert(String id, GtfsRealtime.Alert alert) { // Per the GTFS-rt spec, if an alert has no TimeRanges, than it should always be shown. periods.add(new TimePeriod(0, Long.MAX_VALUE)); } + + AlertPatch patch = new AlertPatch(); + for (EntitySelector informed : alert.getInformedEntityList()) { if (fuzzyTripMatcher != null && informed.hasTrip()) { TripDescriptor trip = fuzzyTripMatcher.match(feedId, informed.getTrip()); @@ -108,6 +111,9 @@ private void handleAlert(String id, GtfsRealtime.Alert alert) { String routeId = null; if (informed.hasRouteId()) { routeId = informed.getRouteId(); + } else if (informed.hasTrip() && informed.getTrip().hasRouteId()) { + String tempRouteId = informed.getTrip().getRouteId(); + routeId = tempRouteId.substring(tempRouteId.indexOf("_")+1); } int direction; @@ -130,10 +136,12 @@ private void handleAlert(String id, GtfsRealtime.Alert alert) { String agencyId = informed.getAgencyId(); if (informed.hasAgencyId()) { agencyId = informed.getAgencyId().intern(); + } else if (informed.hasTrip() && informed.getTrip().hasRouteId()) { + agencyId = informed.getTrip().getRouteId().substring(0, informed.getTrip().getRouteId().indexOf("_")); } - AlertPatch patch = new AlertPatch(); patch.setFeedId(feedId); + if (routeId != null) { patch.setRoute(new AgencyAndId(feedId, routeId)); // Makes no sense to set direction if we don't have a route @@ -150,6 +158,7 @@ private void handleAlert(String id, GtfsRealtime.Alert alert) { if (agencyId != null && routeId == null && tripId == null && stopId == null) { patch.setAgencyId(agencyId); } + patch.setTimePeriods(periods); patch.setAlert(alertText);