/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH licenses this file to you 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 com.graphhopper.reader.gtfs;
import com.graphhopper.routing.util.DefaultEdgeFilter;
import com.graphhopper.storage.Graph;
import com.graphhopper.util.EdgeExplorer;
import com.graphhopper.util.EdgeIterator;
import com.graphhopper.util.EdgeIteratorState;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Iterator;
final class GraphExplorer {
private final EdgeExplorer edgeExplorer;
private final PtFlagEncoder flagEncoder;
private final GtfsStorage gtfsStorage;
private final RealtimeFeed realtimeFeed;
private final boolean reverse;
private final PtTravelTimeWeighting weighting;
GraphExplorer(Graph graph, PtTravelTimeWeighting weighting, PtFlagEncoder flagEncoder, GtfsStorage gtfsStorage, RealtimeFeed realtimeFeed, boolean reverse) {
this.edgeExplorer = graph.createEdgeExplorer(new DefaultEdgeFilter(flagEncoder, reverse, !reverse));
this.flagEncoder = flagEncoder;
this.weighting = weighting;
this.gtfsStorage = gtfsStorage;
this.realtimeFeed = realtimeFeed;
this.reverse = reverse;
}
Iterable<EdgeIteratorState> exploreEdgesAround(Label label) {
return new Iterable<EdgeIteratorState>() {
EdgeIterator edgeIterator = edgeExplorer.setBaseNode(label.adjNode);
@Override
public Iterator<EdgeIteratorState> iterator() {
return new Iterator<EdgeIteratorState>() {
boolean foundEnteredTimeExpandedNetworkEdge = false;
@Override
public boolean hasNext() {
while(edgeIterator.next()) {
final GtfsStorage.EdgeType edgeType = flagEncoder.getEdgeType(edgeIterator.getFlags());
if (!isValidOn(edgeIterator, label.currentTime)) {
continue;
}
if (realtimeFeed.isBlocked(edgeIterator.getEdge())) {
continue;
}
if (edgeType == GtfsStorage.EdgeType.ENTER_TIME_EXPANDED_NETWORK && !reverse) {
if (secondsOnTrafficDay(edgeIterator, label.currentTime) > flagEncoder.getTime(edgeIterator.getFlags())) {
continue;
} else {
if (foundEnteredTimeExpandedNetworkEdge) {
continue;
} else {
foundEnteredTimeExpandedNetworkEdge = true;
}
}
} else if (edgeType == GtfsStorage.EdgeType.LEAVE_TIME_EXPANDED_NETWORK && reverse) {
if (secondsOnTrafficDay(edgeIterator, label.currentTime) < flagEncoder.getTime(edgeIterator.getFlags())) {
continue;
}
}
return true;
}
return false;
}
@Override
public EdgeIteratorState next() {
return edgeIterator;
}
};
}
};
}
long calcTravelTimeMillis(EdgeIteratorState edge, long earliestStartTime) {
GtfsStorage.EdgeType edgeType = flagEncoder.getEdgeType(edge.getFlags());
switch (edgeType) {
case HIGHWAY:
return weighting.calcMillis(edge, false, -1);
case ENTER_TIME_EXPANDED_NETWORK:
if (reverse) {
return 0;
} else {
return waitingTime(edge, earliestStartTime);
}
case LEAVE_TIME_EXPANDED_NETWORK:
if (reverse) {
return -waitingTime(edge, earliestStartTime);
} else {
return 0;
}
default:
return flagEncoder.getTime(edge.getFlags()) * 1000;
}
}
private long waitingTime(EdgeIteratorState edge, long earliestStartTime) {
return (flagEncoder.getTime(edge.getFlags()) - secondsOnTrafficDay(edge, earliestStartTime)) * 1000;
}
private int secondsOnTrafficDay(EdgeIteratorState edge, long instant) {
final ZoneId zoneId = gtfsStorage.getTimeZones().get(flagEncoder.getValidityId(edge.getFlags())).zoneId;
return Instant.ofEpochMilli(instant).atZone(zoneId).toLocalTime().toSecondOfDay();
}
private boolean isValidOn(EdgeIteratorState edge, long instant) {
GtfsStorage.EdgeType edgeType = flagEncoder.getEdgeType(edge.getFlags());
if (edgeType == GtfsStorage.EdgeType.BOARD || edgeType == GtfsStorage.EdgeType.ALIGHT) {
final int validityId = flagEncoder.getValidityId(edge.getFlags());
final GtfsStorage.Validity validity = gtfsStorage.getValidities().get(validityId);
final int trafficDay = (int) ChronoUnit.DAYS.between(validity.start, Instant.ofEpochMilli(instant).atZone(validity.zoneId).toLocalDate());
return validity.validity.get(trafficDay);
} else {
return true;
}
}
}