/** * Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org> * Copyright (C) 2011 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onebusaway.transit_data_federation.impl.beans; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import org.onebusaway.container.cache.Cacheable; import org.onebusaway.geospatial.model.EncodedPolylineBean; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.transit_data.model.AgencyBean; import org.onebusaway.transit_data.model.NameBean; import org.onebusaway.transit_data.model.NameBeanTypes; import org.onebusaway.transit_data.model.RouteBean; import org.onebusaway.transit_data.model.StopBean; import org.onebusaway.transit_data.model.StopGroupBean; import org.onebusaway.transit_data.model.StopGroupingBean; import org.onebusaway.transit_data.model.StopsForRouteBean; import org.onebusaway.transit_data.model.TransitDataConstants; import org.onebusaway.transit_data_federation.impl.DirectedGraph; import org.onebusaway.transit_data_federation.impl.StopGraphComparator; import org.onebusaway.transit_data_federation.model.StopSequence; import org.onebusaway.transit_data_federation.model.StopSequenceCollection; import org.onebusaway.transit_data_federation.model.narrative.RouteCollectionNarrative; import org.onebusaway.transit_data_federation.services.RouteService; import org.onebusaway.transit_data_federation.services.StopSequenceCollectionService; import org.onebusaway.transit_data_federation.services.StopSequencesService; import org.onebusaway.transit_data_federation.services.beans.AgencyBeanService; import org.onebusaway.transit_data_federation.services.beans.RouteBeanService; import org.onebusaway.transit_data_federation.services.beans.ShapeBeanService; import org.onebusaway.transit_data_federation.services.beans.StopBeanService; import org.onebusaway.transit_data_federation.services.blocks.AbstractBlockTripIndex; import org.onebusaway.transit_data_federation.services.blocks.BlockIndexService; import org.onebusaway.transit_data_federation.services.blocks.BlockTripIndex; import org.onebusaway.transit_data_federation.services.blocks.FrequencyBlockTripIndex; import org.onebusaway.transit_data_federation.services.narrative.NarrativeService; import org.onebusaway.transit_data_federation.services.transit_graph.BlockTripEntry; import org.onebusaway.transit_data_federation.services.transit_graph.RouteCollectionEntry; import org.onebusaway.transit_data_federation.services.transit_graph.RouteEntry; import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry; import org.onebusaway.transit_data_federation.services.transit_graph.TransitGraphDao; import org.onebusaway.transit_data_federation.services.transit_graph.TripEntry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component class RouteBeanServiceImpl implements RouteBeanService { private TransitGraphDao _transitGraphDao; private NarrativeService _narrativeService; private AgencyBeanService _agencyBeanService; private StopBeanService _stopBeanService; private ShapeBeanService _shapeBeanService; private RouteService _routeService; private StopSequencesService _stopSequencesService; private StopSequenceCollectionService _stopSequenceBlocksService; private BlockIndexService _blockIndexService; @Autowired public void setTransitGraphDao(TransitGraphDao transitGraphDao) { _transitGraphDao = transitGraphDao; } @Autowired public void setNarrativeService(NarrativeService narrativeService) { _narrativeService = narrativeService; } @Autowired public void setAgencyBeanService(AgencyBeanService agencyBeanService) { _agencyBeanService = agencyBeanService; } @Autowired public void setStopBeanService(StopBeanService stopBeanService) { _stopBeanService = stopBeanService; } @Autowired public void setShapeBeanService(ShapeBeanService shapeBeanService) { _shapeBeanService = shapeBeanService; } @Autowired public void setRouteService(RouteService routeService) { _routeService = routeService; } @Autowired public void setStopSequencesLibrary(StopSequencesService service) { _stopSequencesService = service; } @Autowired public void setStopSequencesBlocksService( StopSequenceCollectionService stopSequenceBlocksService) { _stopSequenceBlocksService = stopSequenceBlocksService; } @Autowired public void setBlockIndexService(BlockIndexService blockIndexService) { _blockIndexService = blockIndexService; } @Cacheable public RouteBean getRouteForId(AgencyAndId id) { RouteCollectionNarrative rc = _narrativeService.getRouteCollectionForId(id); if (rc == null) return null; return getRouteBeanForRouteCollection(id, rc); } @Cacheable public StopsForRouteBean getStopsForRoute(AgencyAndId routeId) { RouteCollectionEntry routeCollectionEntry = _transitGraphDao.getRouteCollectionForId(routeId); RouteCollectionNarrative narrative = _narrativeService.getRouteCollectionForId(routeId); if (routeCollectionEntry == null || narrative == null) return null; return getStopsForRouteCollectionAndNarrative(routeCollectionEntry, narrative); } /**** * Private Methods ****/ private RouteBean getRouteBeanForRouteCollection(AgencyAndId id, RouteCollectionNarrative rc) { RouteBean.Builder bean = RouteBean.builder(); bean.setId(ApplicationBeanLibrary.getId(id)); bean.setShortName(rc.getShortName()); bean.setLongName(rc.getLongName()); bean.setColor(rc.getColor()); bean.setDescription(rc.getDescription()); bean.setTextColor(rc.getTextColor()); bean.setType(rc.getType()); bean.setUrl(rc.getUrl()); AgencyBean agency = _agencyBeanService.getAgencyForId(id.getAgencyId()); bean.setAgency(agency); return bean.create(); } private List<StopBean> getStopBeansForRoute(AgencyAndId routeId) { Collection<AgencyAndId> stopIds = _routeService.getStopsForRouteCollection(routeId); List<StopBean> stops = new ArrayList<StopBean>(); for (AgencyAndId stopId : stopIds) { StopBean stop = _stopBeanService.getStopForId(stopId); stops.add(stop); } return stops; } private StopsForRouteBean getStopsForRouteCollectionAndNarrative( RouteCollectionEntry routeCollection, RouteCollectionNarrative narrative) { StopsForRouteBean result = new StopsForRouteBean(); AgencyAndId routeCollectionId = routeCollection.getId(); result.setRoute(getRouteBeanForRouteCollection(routeCollectionId, narrative)); result.setStops(getStopBeansForRoute(routeCollectionId)); result.setPolylines(getEncodedPolylinesForRoute(routeCollection)); StopGroupingBean directionGrouping = new StopGroupingBean(); directionGrouping.setType(TransitDataConstants.STOP_GROUPING_TYPE_DIRECTION); List<StopGroupBean> directionGroups = new ArrayList<StopGroupBean>(); directionGrouping.setStopGroups(directionGroups); directionGrouping.setOrdered(true); result.addGrouping(directionGrouping); List<BlockTripIndex> blockIndices = _blockIndexService.getBlockTripIndicesForRouteCollectionId(routeCollectionId); List<FrequencyBlockTripIndex> frequencyBlockIndices = _blockIndexService.getFrequencyBlockTripIndicesForRouteCollectionId(routeCollectionId); List<BlockTripEntry> blockTrips = new ArrayList<BlockTripEntry>(); getBlockTripsForIndicesMatchingRouteCollection(blockIndices, routeCollectionId, blockTrips); getBlockTripsForIndicesMatchingRouteCollection(frequencyBlockIndices, routeCollectionId, blockTrips); List<StopSequence> sequences = _stopSequencesService.getStopSequencesForTrips(blockTrips); List<StopSequenceCollection> blocks = _stopSequenceBlocksService.getStopSequencesAsCollections(sequences); for (StopSequenceCollection block : blocks) { NameBean name = new NameBean(NameBeanTypes.DESTINATION, block.getDescription()); List<StopEntry> stops = getStopsInOrder(block); List<String> groupStopIds = new ArrayList<String>(); for (StopEntry stop : stops) groupStopIds.add(ApplicationBeanLibrary.getId(stop.getId())); Set<AgencyAndId> shapeIds = getShapeIdsForStopSequenceBlock(block); List<EncodedPolylineBean> polylines = _shapeBeanService.getMergedPolylinesForShapeIds(shapeIds); StopGroupBean group = new StopGroupBean(); group.setId(block.getPublicId()); group.setName(name); group.setStopIds(groupStopIds); group.setPolylines(polylines); directionGroups.add(group); } sortResult(result); return result; } /** * A block index potentially includes trips with different routes. We only * want the trips matching our route collection. * * @param <T> * @param blockIndices * @param routeCollectionId * @param resultingTrips */ private <T extends AbstractBlockTripIndex> void getBlockTripsForIndicesMatchingRouteCollection( List<T> blockIndices, AgencyAndId routeCollectionId, List<BlockTripEntry> resultingTrips) { for (AbstractBlockTripIndex blockIndex : blockIndices) { for (BlockTripEntry blockTrip : blockIndex.getTrips()) { TripEntry trip = blockTrip.getTrip(); AgencyAndId rcId = trip.getRouteCollection().getId(); if (!rcId.equals(routeCollectionId)) continue; resultingTrips.add(blockTrip); } } } private List<EncodedPolylineBean> getEncodedPolylinesForRoute( RouteCollectionEntry routeCollection) { Set<AgencyAndId> shapeIds = new HashSet<AgencyAndId>(); for (RouteEntry route : routeCollection.getChildren()) { for (TripEntry trip : route.getTrips()) { if (trip.getShapeId() != null) shapeIds.add(trip.getShapeId()); } } return _shapeBeanService.getMergedPolylinesForShapeIds(shapeIds); } private List<StopEntry> getStopsInOrder(StopSequenceCollection block) { DirectedGraph<StopEntry> graph = new DirectedGraph<StopEntry>(); for (StopSequence sequence : block.getStopSequences()) { StopEntry prev = null; for (StopEntry stop : sequence.getStops()) { if (prev != null) { // We do this to avoid cycles if (!graph.isConnected(stop, prev)) graph.addEdge(prev, stop); } prev = stop; } } StopGraphComparator c = new StopGraphComparator(graph); return graph.getTopologicalSort(c); } private Set<AgencyAndId> getShapeIdsForStopSequenceBlock( StopSequenceCollection block) { Set<AgencyAndId> shapeIds = new HashSet<AgencyAndId>(); for (StopSequence sequence : block.getStopSequences()) { for (BlockTripEntry blockTrip : sequence.getTrips()) { TripEntry trip = blockTrip.getTrip(); AgencyAndId shapeId = trip.getShapeId(); if (shapeId != null && shapeId.hasValues()) shapeIds.add(shapeId); } } return shapeIds; } private void sortResult(StopsForRouteBean result) { Collections.sort(result.getStops(), new StopBeanIdComparator()); Collections.sort(result.getStopGroupings(), new Comparator<StopGroupingBean>() { public int compare(StopGroupingBean o1, StopGroupingBean o2) { return o1.getType().compareTo(o2.getType()); } }); for (StopGroupingBean grouping : result.getStopGroupings()) { Collections.sort(grouping.getStopGroups(), new Comparator<StopGroupBean>() { public int compare(StopGroupBean o1, StopGroupBean o2) { return getName(o1).compareTo(getName(o2)); } private String getName(StopGroupBean bean) { StringBuilder b = new StringBuilder(); for (String name : bean.getName().getNames()) b.append(name); return b.toString(); } }); } } }