RoadAccessRestrictionsGraphStorageBuilder.java
/*
* This file is part of Openrouteservice.
*
* Openrouteservice is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library;
* if not, see <https://www.gnu.org/licenses/>.
*/
package org.heigit.ors.routing.graphhopper.extensions.storages.builders;
import com.graphhopper.GraphHopper;
import com.graphhopper.reader.ReaderWay;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.storage.GraphExtension;
import com.graphhopper.util.EdgeIteratorState;
import org.heigit.ors.routing.RoutingProfileType;
import org.heigit.ors.routing.graphhopper.extensions.AccessRestrictionType;
import org.heigit.ors.routing.graphhopper.extensions.storages.RoadAccessRestrictionsGraphStorage;
import org.locationtech.jts.geom.Coordinate;
import java.util.*;
/**
* Builder for road access restrictions information. The purpose is to record for edges any restrictions that are in
* place for the particular vehicle type that is being processed for the profile.
*/
public class RoadAccessRestrictionsGraphStorageBuilder extends AbstractGraphStorageBuilder {
private static final String KEY_USE_FOR_WARNINGS = "use_for_warnings";
private static final String VAL_BICYCLE = "bicycle";
private static final String VAL_ACCESS = "access";
private static final String VAL_MOTOR_VEHICLE = "motor_vehicle";
private RoadAccessRestrictionsGraphStorage storage;
private boolean hasRestrictions = false;
private int restrictions;
private final List<String> accessRestrictedTags = new ArrayList<>(5);
private final List<String> motorCarTags = new ArrayList<>(5);
private final List<String> motorCycleTags = new ArrayList<>(5);
private final Set<String> restrictedValues = new HashSet<>(5);
private final Set<String> permissiveValues = new HashSet<>(5);
private int profileType;
public RoadAccessRestrictionsGraphStorageBuilder() {
accessRestrictedTags.addAll(Arrays.asList("motorcar", VAL_MOTOR_VEHICLE, "vehicle", VAL_ACCESS, VAL_BICYCLE, "foot"));
motorCarTags.addAll(Arrays.asList("motorcar", VAL_MOTOR_VEHICLE));
motorCycleTags.addAll(Arrays.asList("motorcycle", VAL_MOTOR_VEHICLE));
restrictedValues.add("private");
restrictedValues.add("no");
restrictedValues.add("restricted");
restrictedValues.add("military");
restrictedValues.add("destination");
restrictedValues.add("customers");
restrictedValues.add("emergency");
restrictedValues.add("permissive");
permissiveValues.add("yes");
permissiveValues.add("designated");
permissiveValues.add("official");
}
/**
* Initialise the road access restrictions graph storage builder for a profile and set the profile type to be that
* specified.
*
* @param graphhopper The graphhopper instance being used (not used)
* @param profileType The id of the profile type that the RoadAccessRestrictions are for
* @return The RoadAccessRestrictionStorage object created as part of the initialisation
* @throws Exception
*/
public GraphExtension init(GraphHopper graphhopper, int profileType) throws Exception {
if (storage != null)
throw new Exception("GraphStorageBuilder has been already initialized.");
this.profileType = profileType;
storage = new RoadAccessRestrictionsGraphStorage();
if (parameters.containsKey(KEY_USE_FOR_WARNINGS))
storage.setIsUsedForWarning(Boolean.parseBoolean(parameters.get(KEY_USE_FOR_WARNINGS)));
return storage;
}
/**
* Initialise the road access restrictions graph storage builder for a profile
*
* @param graphhopper The graphhopper instance being used
* @return The RoadAccessRestrictionStorage object created as part of the initialisation
* @throws Exception
*/
public GraphExtension init(GraphHopper graphhopper) throws Exception {
if (storage != null)
throw new Exception("GraphStorageBuilder has been already initialized.");
// extract profiles from GraphHopper instance
EncodingManager encMgr = graphhopper.getEncodingManager();
List<FlagEncoder> encoders = encMgr.fetchEdgeEncoders();
int[] profileTypes = new int[encoders.size()];
int i = 0;
for (FlagEncoder enc : encoders) {
profileTypes[i] = RoutingProfileType.getFromEncoderName(enc.toString());
i++;
}
profileType = profileTypes[0];
storage = new RoadAccessRestrictionsGraphStorage();
if (parameters.containsKey(KEY_USE_FOR_WARNINGS))
storage.setIsUsedForWarning(Boolean.parseBoolean(parameters.get(KEY_USE_FOR_WARNINGS)));
return storage;
}
public void processWay(ReaderWay way) {
this.processWay(way, null, null);
}
/**
* Process the road access restrictions of a way feature ready for processing into edges. It checks to see if there
* are restrictions present in the form of tags (e.g. access=private) and then stores the information accordingly.
* It first checks if there have already been restrictions recorded and if so clears them.
*
* @param way The way to be processed
* @param coords List of coordinates for the way (not used)
* @param nodeTags List of node ids and the key value pairs for the tags of that node. These values can be used to
* apply restrictions on a way introduced by items like lift gates that are nodes on the way
*/
@Override
public void processWay(ReaderWay way, Coordinate[] coords, Map<Integer, Map<String, String>> nodeTags) {
if (hasRestrictions) {
hasRestrictions = false;
restrictions = 0;
}
if (nodeTags != null) {
for (Map<String, String> tagPairs : nodeTags.values()) {
for (Map.Entry<String, String> pair : tagPairs.entrySet()) {
way.setTag(pair.getKey(), pair.getValue());
}
}
}
if (way.hasTag(accessRestrictedTags, restrictedValues)) {
hasRestrictions = true;
if (RoutingProfileType.isDriving(profileType))
restrictions = isAccessAllowed(way, motorCarTags) ? 0 : getRestrictionType(way, motorCarTags);
if (profileType == RoutingProfileType.DRIVING_MOTORCYCLE)
restrictions = isAccessAllowed(way, motorCycleTags) ? 0 : getRestrictionType(way, motorCycleTags);
if (RoutingProfileType.isCycling(profileType))
restrictions = isAccessAllowed(way, VAL_BICYCLE) ? 0 : getRestrictionType(way, VAL_BICYCLE);
if (RoutingProfileType.isPedestrian(profileType))
restrictions = isAccessAllowed(way, "foot") ? 0 : getRestrictionType(way, "foot");
}
}
/**
* Get the type of restrictions that have been set on the way.
*
* @param way The way to be checked
* @param tags The tags(keys) that should be accessed for the access restrictions
* @return 0 if no restriction, else the integer encoded restriction value for the way
*/
private int getRestrictionType(ReaderWay way, List<String> tags) {
int res = 0;
String tagValue = way.getTag(VAL_ACCESS);
if (tagValue != null)
res = updateRestriction(res, tagValue);
if (tags != null) {
for (String key : tags) {
tagValue = way.getTag(key);
res = updateRestriction(res, tagValue);
}
}
return res;
}
/**
* Get the type of restrictions that have been set on the way.
*
* @param way The way to be checked
* @param tag The tag(key) that should be accessed for the access restrictions
* @return 0 if no restriction, else the integer encoded restriction value for the way
*/
private int getRestrictionType(ReaderWay way, String tag) {
int res = 0;
String tagValue = way.getTag(VAL_ACCESS);
if (tagValue != null)
res = updateRestriction(res, tagValue);
tagValue = way.getTag(tag);
res = updateRestriction(res, tagValue);
return res;
}
/**
* Take the encoded restriction value and update it with the passed restriction value
*
* @param encodedRestrictions Integer representation of the current restrictions
* @param restrictionValue The new restriction to be applied
* @return An integer encoded representation of all restrictions that have been set
*/
private int updateRestriction(int encodedRestrictions, String restrictionValue) {
int res = encodedRestrictions;
if (restrictionValue != null && !restrictionValue.isEmpty()) {
switch (restrictionValue) {
case "no" -> res |= AccessRestrictionType.NO;
case "destination" -> res |= AccessRestrictionType.DESTINATION;
case "private" -> res |= AccessRestrictionType.PRIVATE;
case "permissive" -> res |= AccessRestrictionType.PERMISSIVE;
case "delivery" -> res |= AccessRestrictionType.DELIVERY;
case "customers" -> res |= AccessRestrictionType.CUSTOMERS;
default -> {
}
}
}
return res;
}
/**
* Check if access is allowed on the way. e.g. it would check if motor_car=yes/permissive/destination etc. is set
*
* @param way The OSM way to be checked
* @param tagNames The tags (keys) to be checked
* @return Whether access is allowed on the way
*/
private boolean isAccessAllowed(ReaderWay way, List<String> tagNames) {
return way.hasTag(tagNames, permissiveValues);
}
/**
* Check if access is allowed on the way. e.g. it would check if motor_car=yes/permissive/destination etc. is set
*
* @param way The OSM way to be checked
* @param tagName The single tag (key) to be checked
* @return Whether access is allowed on the way
*/
private boolean isAccessAllowed(ReaderWay way, String tagName) {
return way.hasTag(tagName, permissiveValues);
}
public void processEdge(ReaderWay way, EdgeIteratorState edge) {
storage.setEdgeValue(edge.getEdge(), restrictions);
}
public final int getRestrictions() {
return restrictions;
}
@Override
public String getName() {
return "roadaccessrestrictions";
}
}