/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* 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.tripplanner;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.onebusaway.collections.CollectionsLibrary;
import org.onebusaway.exceptions.OutOfServiceAreaServiceException;
import org.onebusaway.exceptions.ServiceException;
import org.onebusaway.geospatial.model.CoordinateBounds;
import org.onebusaway.geospatial.model.CoordinatePoint;
import org.onebusaway.geospatial.services.SphericalGeometryLibrary;
import org.onebusaway.transit_data.model.tripplanning.TransitLocationBean;
import org.onebusaway.transit_data_federation.impl.otp.OBAState;
import org.onebusaway.transit_data_federation.impl.otp.OBATraverseOptions;
import org.onebusaway.transit_data_federation.impl.otp.RemainingWeightHeuristicImpl;
import org.onebusaway.transit_data_federation.impl.otp.SearchTerminationStrategyImpl;
import org.onebusaway.transit_data_federation.impl.otp.TPRemainingWeightHeuristicImpl;
import org.onebusaway.transit_data_federation.impl.otp.TripSequenceShortestPathTree;
import org.onebusaway.transit_data_federation.impl.otp.graph.WalkFromStopVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.WalkToStopVertex;
import org.onebusaway.transit_data_federation.impl.otp.graph.tp.TPQueryData;
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.tripplanner.ItinerariesService;
import org.onebusaway.transit_data_federation.services.tripplanner.TransferPatternService;
import org.opentripplanner.routing.algorithm.GenericAStar;
import org.opentripplanner.routing.algorithm.strategies.GenericAStarFactory;
import org.opentripplanner.routing.algorithm.strategies.SkipTraverseResultStrategy;
import org.opentripplanner.routing.core.Graph;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.TraverseModeSet;
import org.opentripplanner.routing.core.TraverseOptions;
import org.opentripplanner.routing.core.Vertex;
import org.opentripplanner.routing.services.GraphService;
import org.opentripplanner.routing.services.PathService;
import org.opentripplanner.routing.services.StreetVertexIndexService;
import org.opentripplanner.routing.spt.GraphPath;
import org.opentripplanner.routing.spt.ShortestPathTree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.vividsolutions.jts.geom.Coordinate;
@Component
class ItinerariesServiceImpl implements ItinerariesService {
private TransitGraphDao _transitGraphDao;
private double _walkSearchInitialRadius = 800;
private double _walkSearchStep = 500;
private double _walkSearchMaxRadius = 2500;
private PathService _pathService;
private GraphService _graphService;
private StreetVertexIndexService _streetVertexIndexService;
private TransferPatternService _transferPathService;
@Autowired
public void setTransitGraphDao(TransitGraphDao transitGraphDao) {
_transitGraphDao = transitGraphDao;
}
@Autowired
public void setPathService(PathService pathService) {
_pathService = pathService;
}
@Autowired
public void setGraphService(GraphService graphService) {
_graphService = graphService;
}
@Autowired
public void setStreetVertexIndexService(
StreetVertexIndexService streetVertexIndexService) {
_streetVertexIndexService = streetVertexIndexService;
}
@Autowired
public void setTransf(TransferPatternService transferPathService) {
_transferPathService = transferPathService;
}
/****
* {@link ItinerariesService} Interface
****/
public List<GraphPath> getItinerariesBetween(TransitLocationBean from,
TransitLocationBean to, long targetTime, OBATraverseOptions options)
throws ServiceException {
Vertex fromVertex = getTransitLocationAsVertex(from, options);
Vertex toVertex = getTransitLocationAsVertex(to, options);
if (fromVertex == null || toVertex == null)
throw new OutOfServiceAreaServiceException();
Vertex v = options.isArriveBy() ? toVertex : fromVertex;
Vertex target = options.isArriveBy() ? fromVertex : toVertex;
State state = new OBAState(targetTime, v, options);
if (_transferPathService.isEnabled()) {
return getTransferPatternItinerariesBetween(fromVertex, toVertex,
new Date(targetTime), options);
} else {
options.remainingWeightHeuristic = new RemainingWeightHeuristicImpl();
options.aStarSearchFactory = new GenericAStarFactoryImpl();
return _pathService.plan(state, target, 1);
}
}
@Override
public GraphPath getWalkingItineraryBetweenStops(StopEntry fromStop,
StopEntry toStop, Date time, TraverseOptions options) {
WalkFromStopVertex fromVertex = getWalkFromStopVertexForStop(fromStop);
WalkToStopVertex toVertex = getWalkToStopVertexForStop(toStop);
return getWalkingItineraryBetweenVertices(fromVertex, toVertex, time,
options);
}
@Override
public GraphPath getWalkingItineraryBetweenPoints(CoordinatePoint from,
CoordinatePoint to, Date time, TraverseOptions options) {
Coordinate a = new Coordinate(from.getLon(), from.getLat());
Coordinate b = new Coordinate(to.getLon(), to.getLat());
Vertex v1 = _streetVertexIndexService.getClosestVertex(a, options);
Vertex v2 = _streetVertexIndexService.getClosestVertex(b, options);
if (v1 == null || v2 == null)
return null;
return getWalkingItineraryBetweenVertices(v1, v2, time, options);
}
@Override
public GraphPath getWalkingItineraryBetweenVertices(Vertex from, Vertex to,
Date time, TraverseOptions options) {
options = options.clone();
/**
* Set walk only
*/
TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK);
options.setModes(modes);
Vertex origin = options.isArriveBy() ? to : from;
Vertex target = options.isArriveBy() ? from : to;
State state = new OBAState(time.getTime(), origin, options);
List<GraphPath> paths = _pathService.plan(state, target, 1);
if (CollectionsLibrary.isEmpty(paths))
return null;
return paths.get(0);
}
/****
* Private Methods
****/
private Vertex getTransitLocationAsVertex(TransitLocationBean from,
OBATraverseOptions options) {
Coordinate c = new Coordinate(from.getLon(), from.getLat());
return _streetVertexIndexService.getClosestVertex(c, options);
}
private List<GraphPath> getTransferPatternItinerariesBetween(
Vertex fromVertex, Vertex toVertex, Date time, OBATraverseOptions options) {
Set<StopEntry> sourceStops = Collections.emptySet();
Set<StopEntry> destStops = Collections.emptySet();
if (options.isArriveBy()) {
List<StopEntry> stops = getNearbyStops(fromVertex, options, time, true);
sourceStops = new HashSet<StopEntry>(stops);
if (sourceStops.isEmpty())
return Collections.emptyList();
} else {
List<StopEntry> stops = getNearbyStops(toVertex, options, time, false);
destStops = new HashSet<StopEntry>(stops);
if (destStops.isEmpty())
return Collections.emptyList();
}
TPQueryData queryData = new TPQueryData(sourceStops, destStops);
options.putExtension(TPQueryData.class, queryData);
Graph graph = _graphService.getGraph();
Vertex origin = options.isArriveBy() ? toVertex : fromVertex;
Vertex target = options.isArriveBy() ? fromVertex : toVertex;
State init = new OBAState(time.getTime(), origin, options);
options.remainingWeightHeuristic = new TPRemainingWeightHeuristicImpl();
GenericAStar search = new GenericAStar();
search.setSkipTraverseResultStrategy(new SkipVertexImpl());
search.setSearchTerminationStrategy(new SearchTerminationStrategyImpl());
search.setShortestPathTreeFactory(TripSequenceShortestPathTree.FACTORY);
ShortestPathTree spt = search.getShortestPathTree(graph, init, target);
return spt.getPaths(target, true);
}
public List<StopEntry> getNearbyStops(Vertex v, TraverseOptions options,
Date time, boolean isOrigin) {
double initialRadius = Math.min(_walkSearchInitialRadius,
options.maxWalkDistance);
CoordinatePoint location = new CoordinatePoint(v.getY(), v.getX());
for (double radius = initialRadius; radius < _walkSearchMaxRadius; radius += _walkSearchStep) {
CoordinateBounds bounds = SphericalGeometryLibrary.bounds(location,
radius);
List<StopEntry> stops = _transitGraphDao.getStopsByLocation(bounds);
if (stops.isEmpty())
continue;
return stops;
}
return Collections.emptyList();
}
private WalkFromStopVertex getWalkFromStopVertexForStop(StopEntry stop) {
Graph graph = _graphService.getGraph();
String label = WalkFromStopVertex.getVertexLabelForStop(stop);
return (WalkFromStopVertex) graph.getVertex(label);
}
private WalkToStopVertex getWalkToStopVertexForStop(StopEntry stop) {
Graph graph = _graphService.getGraph();
String label = WalkToStopVertex.getVertexLabelForStop(stop);
return (WalkToStopVertex) graph.getVertex(label);
}
private static class SkipVertexImpl implements SkipTraverseResultStrategy {
@Override
public boolean shouldSkipTraversalResult(Vertex origin, Vertex target, State parent,
State current, ShortestPathTree spt, TraverseOptions traverseOptions) {
if (traverseOptions.maxWalkDistance > 0
&& current.getWalkDistance() > traverseOptions.maxWalkDistance)
return true;
return false;
}
}
private static class GenericAStarFactoryImpl implements GenericAStarFactory {
@Override
public GenericAStar createAStarInstance() {
GenericAStar instance = new GenericAStar();
instance.setSearchTerminationStrategy(new SearchTerminationStrategyImpl());
instance.setShortestPathTreeFactory(TripSequenceShortestPathTree.FACTORY);
return instance;
}
}
}