/* * 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.routing.util; import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.weighting.PriorityWeighting; import com.graphhopper.util.PMap; import java.util.*; import static com.graphhopper.routing.util.PriorityCode.*; /** * Defines bit layout for pedestrians (speed, access, surface, ...). Here we put a penalty on unsafe * roads only. If you wish to also prefer routes due to beauty like hiking routes use the * HikeFlagEncoder instead. * <p> * * @author Peter Karich * @author Nop * @author Karl Hübner */ public class FootFlagEncoder extends AbstractFlagEncoder { static final int SLOW_SPEED = 2; static final int MEAN_SPEED = 5; static final int FERRY_SPEED = 10; final Set<String> safeHighwayTags = new HashSet<String>(); final Set<String> allowedHighwayTags = new HashSet<String>(); final Set<String> avoidHighwayTags = new HashSet<String>(); // convert network tag of hiking routes into a way route code final Map<String, Integer> hikingNetworkToCode = new HashMap<String, Integer>(); protected HashSet<String> sidewalkValues = new HashSet<String>(5); protected HashSet<String> sidewalksNoValues = new HashSet<String>(5); private EncodedValue priorityWayEncoder; private EncodedValue relationCodeEncoder; /** * Should be only instantiated via EncodingManager */ public FootFlagEncoder() { this(4, 1); } public FootFlagEncoder(PMap properties) { this((int) properties.getLong("speedBits", 4), properties.getDouble("speedFactor", 1)); this.properties = properties; this.setBlockFords(properties.getBool("block_fords", true)); } public FootFlagEncoder(String propertiesStr) { this(new PMap(propertiesStr)); } public FootFlagEncoder(int speedBits, double speedFactor) { super(speedBits, speedFactor, 0); restrictions.addAll(Arrays.asList("foot", "access")); restrictedValues.add("private"); restrictedValues.add("no"); restrictedValues.add("restricted"); restrictedValues.add("military"); restrictedValues.add("emergency"); intendedValues.add("yes"); intendedValues.add("designated"); intendedValues.add("official"); intendedValues.add("permissive"); sidewalksNoValues.add("no"); sidewalksNoValues.add("none"); // see #712 sidewalksNoValues.add("separate"); sidewalkValues.add("yes"); sidewalkValues.add("both"); sidewalkValues.add("left"); sidewalkValues.add("right"); setBlockByDefault(false); potentialBarriers.add("gate"); safeHighwayTags.add("footway"); safeHighwayTags.add("path"); safeHighwayTags.add("steps"); safeHighwayTags.add("pedestrian"); safeHighwayTags.add("living_street"); safeHighwayTags.add("track"); safeHighwayTags.add("residential"); safeHighwayTags.add("service"); avoidHighwayTags.add("trunk"); avoidHighwayTags.add("trunk_link"); avoidHighwayTags.add("primary"); avoidHighwayTags.add("primary_link"); avoidHighwayTags.add("secondary"); avoidHighwayTags.add("secondary_link"); avoidHighwayTags.add("tertiary"); avoidHighwayTags.add("tertiary_link"); // for now no explicit avoiding #257 //avoidHighwayTags.add("cycleway"); allowedHighwayTags.addAll(safeHighwayTags); allowedHighwayTags.addAll(avoidHighwayTags); allowedHighwayTags.add("cycleway"); allowedHighwayTags.add("unclassified"); allowedHighwayTags.add("road"); // disallowed in some countries //allowedHighwayTags.add("bridleway"); hikingNetworkToCode.put("iwn", UNCHANGED.getValue()); hikingNetworkToCode.put("nwn", UNCHANGED.getValue()); hikingNetworkToCode.put("rwn", UNCHANGED.getValue()); hikingNetworkToCode.put("lwn", UNCHANGED.getValue()); maxPossibleSpeed = FERRY_SPEED; init(); } @Override public int getVersion() { return 3; } @Override public int defineWayBits(int index, int shift) { // first two bits are reserved for route handling in superclass shift = super.defineWayBits(index, shift); // larger value required - ferries are faster than pedestrians speedEncoder = new EncodedDoubleValue("Speed", shift, speedBits, speedFactor, MEAN_SPEED, maxPossibleSpeed); shift += speedEncoder.getBits(); priorityWayEncoder = new EncodedValue("PreferWay", shift, 3, 1, 0, 7); shift += priorityWayEncoder.getBits(); return shift; } @Override public int defineRelationBits(int index, int shift) { relationCodeEncoder = new EncodedValue("RelationCode", shift, 3, 1, 0, 7); return shift + relationCodeEncoder.getBits(); } /** * Foot flag encoder does not provide any turn cost / restrictions */ @Override public int defineTurnBits(int index, int shift) { return shift; } /** * Foot flag encoder does not provide any turn cost / restrictions * <p> * * @return <code>false</code> */ @Override public boolean isTurnRestricted(long flag) { return false; } /** * Foot flag encoder does not provide any turn cost / restrictions * <p> * * @return 0 */ @Override public double getTurnCost(long flag) { return 0; } @Override public long getTurnFlags(boolean restricted, double costs) { return 0; } /** * Some ways are okay but not separate for pedestrians. * <p> */ @Override public long acceptWay(ReaderWay way) { String highwayValue = way.getTag("highway"); if (highwayValue == null) { if (way.hasTag("route", ferries)) { String footTag = way.getTag("foot"); if (footTag == null || "yes".equals(footTag)) return acceptBit | ferryBit; } // special case not for all acceptedRailways, only platform if (way.hasTag("railway", "platform")) return acceptBit; return 0; } String sacScale = way.getTag("sac_scale"); if (sacScale != null) { if (!"hiking".equals(sacScale) && !"mountain_hiking".equals(sacScale) && !"demanding_mountain_hiking".equals(sacScale) && !"alpine_hiking".equals(sacScale)) // other scales are too dangerous, see http://wiki.openstreetmap.org/wiki/Key:sac_scale return 0; } // no need to evaluate ferries or fords - already included here if (way.hasTag("foot", intendedValues)) return acceptBit; // check access restrictions if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) return 0; if (way.hasTag("sidewalk", sidewalkValues)) return acceptBit; if (!allowedHighwayTags.contains(highwayValue)) return 0; if (way.hasTag("motorroad", "yes")) return 0; // do not get our feet wet, "yes" is already included above if (isBlockFords() && (way.hasTag("highway", "ford") || way.hasTag("ford"))) return 0; if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way)) return 0; return acceptBit; } @Override public long handleRelationTags(ReaderRelation relation, long oldRelationFlags) { int code = 0; if (relation.hasTag("route", "hiking") || relation.hasTag("route", "foot")) { Integer val = hikingNetworkToCode.get(relation.getTag("network")); if (val != null) code = val; else code = hikingNetworkToCode.get("lwn"); } else if (relation.hasTag("route", "ferry")) { code = PriorityCode.AVOID_IF_POSSIBLE.getValue(); } int oldCode = (int) relationCodeEncoder.getValue(oldRelationFlags); if (oldCode < code) return relationCodeEncoder.setValue(0, code); return oldRelationFlags; } @Override public long handleWayTags(ReaderWay way, long allowed, long relationFlags) { if (!isAccept(allowed)) return 0; long flags = 0; if (!isFerry(allowed)) { String sacScale = way.getTag("sac_scale"); if (sacScale != null) { if ("hiking".equals(sacScale)) flags = speedEncoder.setDoubleValue(flags, MEAN_SPEED); else flags = speedEncoder.setDoubleValue(flags, SLOW_SPEED); } else { flags = speedEncoder.setDoubleValue(flags, MEAN_SPEED); } flags |= directionBitMask; boolean isRoundabout = way.hasTag("junction", "roundabout"); if (isRoundabout) flags = setBool(flags, K_ROUNDABOUT, true); } else { double ferrySpeed = getFerrySpeed(way, SLOW_SPEED, MEAN_SPEED, FERRY_SPEED); flags = setSpeed(flags, ferrySpeed); flags |= directionBitMask; } int priorityFromRelation = 0; if (relationFlags != 0) priorityFromRelation = (int) relationCodeEncoder.getValue(relationFlags); flags = priorityWayEncoder.setValue(flags, handlePriority(way, priorityFromRelation)); return flags; } @Override public double getDouble(long flags, int key) { switch (key) { case PriorityWeighting.KEY: return (double) priorityWayEncoder.getValue(flags) / BEST.getValue(); default: return super.getDouble(flags, key); } } protected int handlePriority(ReaderWay way, int priorityFromRelation) { TreeMap<Double, Integer> weightToPrioMap = new TreeMap<Double, Integer>(); if (priorityFromRelation == 0) weightToPrioMap.put(0d, UNCHANGED.getValue()); else weightToPrioMap.put(110d, priorityFromRelation); collect(way, weightToPrioMap); // pick priority with biggest order value return weightToPrioMap.lastEntry().getValue(); } /** * @param weightToPrioMap associate a weight with every priority. This sorted map allows * subclasses to 'insert' more important priorities as well as overwrite determined priorities. */ void collect(ReaderWay way, TreeMap<Double, Integer> weightToPrioMap) { String highway = way.getTag("highway"); if (way.hasTag("foot", "designated")) weightToPrioMap.put(100d, PREFER.getValue()); double maxSpeed = getMaxSpeed(way); if (safeHighwayTags.contains(highway) || maxSpeed > 0 && maxSpeed <= 20) { weightToPrioMap.put(40d, PREFER.getValue()); if (way.hasTag("tunnel", intendedValues)) { if (way.hasTag("sidewalk", sidewalksNoValues)) weightToPrioMap.put(40d, AVOID_IF_POSSIBLE.getValue()); else weightToPrioMap.put(40d, UNCHANGED.getValue()); } } else if (maxSpeed > 50 || avoidHighwayTags.contains(highway)) { if (!way.hasTag("sidewalk", sidewalkValues)) weightToPrioMap.put(45d, AVOID_IF_POSSIBLE.getValue()); } if (way.hasTag("bicycle", "official") || way.hasTag("bicycle", "designated")) weightToPrioMap.put(44d, AVOID_IF_POSSIBLE.getValue()); } @Override public boolean supports(Class<?> feature) { if (super.supports(feature)) return true; return PriorityWeighting.class.isAssignableFrom(feature); } @Override public String toString() { return "foot"; } }