All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.routing.util.AbstractFlagEncoder Maven / Gradle / Ivy

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 0.7.0
Show newest version
/*
 *  Licensed to GraphHopper and Peter Karich under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for 
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper 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.osm.conditional.ConditionalTagsInspector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.graphhopper.reader.OSMNode;
import com.graphhopper.reader.OSMWay;
import com.graphhopper.reader.OSMRelation;
import com.graphhopper.util.*;

import java.util.*;

/**
 * Abstract class which handles flag decoding and encoding. Every encoder should be registered to a
 * EncodingManager to be usable. If you want the full long to be stored you need to enable this in
 * the GraphHopperStorage.
 * 

* @author Peter Karich * @author Nop * @see EncodingManager */ public abstract class AbstractFlagEncoder implements FlagEncoder, TurnCostEncoder { private final static Logger logger = LoggerFactory.getLogger(AbstractFlagEncoder.class); protected final static int K_FORWARD = 0, K_BACKWARD = 1; /* Edge Flag Encoder fields */ private long nodeBitMask; private long wayBitMask; private long relBitMask; protected long forwardBit; protected long backwardBit; protected long directionBitMask; protected long roundaboutBit; protected EncodedDoubleValue speedEncoder; // bit to signal that way is accepted protected long acceptBit; protected long ferryBit; protected PMap properties; // This value determines the maximal possible speed of any road regardless the maxspeed value // lower values allow more compact representation of the routing graph protected int maxPossibleSpeed; private EncodedValue turnCostEncoder; private long turnRestrictionBit; private final int maxTurnCosts; /* processing properties (to be initialized lazy when needed) */ protected EdgeExplorer edgeOutExplorer; protected EdgeExplorer edgeInExplorer; /* restriction definitions where order is important */ protected final List restrictions = new ArrayList(5); protected final Set intendedValues = new HashSet(5); protected final Set restrictedValues = new HashSet(5); protected final Set ferries = new HashSet(5); protected final Set oneways = new HashSet(5); protected final Set acceptedRailways = new HashSet(5); // http://wiki.openstreetmap.org/wiki/Mapfeatures#Barrier protected final Set absoluteBarriers = new HashSet(5); protected final Set potentialBarriers = new HashSet(5); private boolean blockByDefault = true; private boolean blockFords = true; protected final int speedBits; protected final double speedFactor; private boolean registered; protected ConditionalTagsInspector conditionalTagsInspector; public AbstractFlagEncoder( PMap properties ) { throw new RuntimeException("This method must be overridden in derived classes"); } public AbstractFlagEncoder( String propertiesStr ) { this(new PMap(propertiesStr)); } /** * @param speedBits specify the number of bits used for speed * @param speedFactor specify the factor to multiple the stored value (can be used to increase * or decrease accuracy of speed value) * @param maxTurnCosts specify the maximum value used for turn costs, if this value is reached a * turn is forbidden and results in costs of positive infinity. */ protected AbstractFlagEncoder( int speedBits, double speedFactor, int maxTurnCosts ) { this.maxTurnCosts = maxTurnCosts <= 0 ? 0 : maxTurnCosts; this.speedBits = speedBits; this.speedFactor = speedFactor; oneways.add("yes"); oneways.add("true"); oneways.add("1"); oneways.add("-1"); ferries.add("shuttle_train"); ferries.add("ferry"); acceptedRailways.add("tram"); acceptedRailways.add("abandoned"); acceptedRailways.add("abandoned_tram"); acceptedRailways.add("disused"); // http://wiki.openstreetmap.org/wiki/Demolished_Railway acceptedRailways.add("dismantled"); acceptedRailways.add("razed"); acceptedRailways.add("historic"); acceptedRailways.add("obliterated"); } public void setRegistered( boolean registered ) { this.registered = registered; } @Override public boolean isRegistered() { return registered; } /** * Should potential barriers block when no access limits are given? */ public void setBlockByDefault( boolean blockByDefault ) { this.blockByDefault = blockByDefault; } public void setBlockFords( boolean blockFords ) { this.blockFords = blockFords; } public boolean isBlockFords() { return blockFords; } /** * Defines the bits for the node flags, which are currently used for barriers only. *

* @return incremented shift value pointing behind the last used bit */ public int defineNodeBits( int index, int shift ) { return shift; } /** * Defines bits used for edge flags used for access, speed etc. *

* @param shift bit offset for the first bit used by this encoder * @return incremented shift value pointing behind the last used bit */ public int defineWayBits( int index, int shift ) { if (isRegistered()) throw new IllegalStateException("You must not register a FlagEncoder (" + toString() + ") twice!"); setRegistered(true); // define the first 2 speedBits in flags for routing forwardBit = 1L << shift; backwardBit = 2L << shift; directionBitMask = 3L << shift; shift += 2; roundaboutBit = 1L << shift; shift++; // define internal flags for parsing index *= 2; acceptBit = 1L << index; ferryBit = 2L << index; return shift; } /** * Defines the bits which are used for relation flags. *

* @return incremented shift value pointing behind the last used bit */ public int defineRelationBits( int index, int shift ) { return shift; } /** * Analyze the properties of a relation and create the routing flags for the second read step. * In the pre-parsing step this method will be called to determine the useful relation tags. *

*/ public abstract long handleRelationTags( OSMRelation relation, long oldRelationFlags ); /** * Decide whether a way is routable for a given mode of travel. This skips some ways before * handleWayTags is called. *

* @return the encoded value to indicate if this encoder allows travel or not. */ public abstract long acceptWay( OSMWay way ); /** * Analyze properties of a way and create the routing flags. This method is called in the second * parsing step. */ public abstract long handleWayTags( OSMWay way, long allowed, long relationFlags ); /** * Parse tags on nodes. Node tags can add to speed (like traffic_signals) where the value is * strict negative or blocks access (like a barrier), then the value is strict positive.This * method is called in the second parsing step. */ public long handleNodeTags( OSMNode node ) { // absolute barriers always block if (node.hasTag("barrier", absoluteBarriers)) return directionBitMask; // movable barriers block if they are not marked as passable if (node.hasTag("barrier", potentialBarriers)) { boolean locked = false; if (node.hasTag("locked", "yes")) locked = true; for (String res : restrictions) { if (!locked && node.hasTag(res, intendedValues)) return 0; if (node.hasTag(res, restrictedValues)) return directionBitMask; } if (blockByDefault) return directionBitMask; } // In case explicit flag ford=no, don't block if (blockFords && (node.hasTag("highway", "ford") || node.hasTag("ford")) && !node.hasTag(restrictions, intendedValues) && !node.hasTag("ford", "no")) { return directionBitMask; } return 0; } @Override public InstructionAnnotation getAnnotation( long flags, Translation tr ) { return InstructionAnnotation.EMPTY; } /** * Swapping directions means swapping bits which are dependent on the direction of an edge like * the access bits. But also direction dependent speed values should be swapped too. Keep in * mind that this method is performance critical! */ public long reverseFlags( long flags ) { long dir = flags & directionBitMask; if (dir == directionBitMask || dir == 0) return flags; return flags ^ directionBitMask; } /** * Sets default flags with specified access. */ public long flagsDefault( boolean forward, boolean backward ) { long flags = speedEncoder.setDefaultValue(0); return setAccess(flags, forward, backward); } @Override public long setAccess( long flags, boolean forward, boolean backward ) { return setBool(setBool(flags, K_BACKWARD, backward), K_FORWARD, forward); } @Override public long setSpeed( long flags, double speed ) { if (speed < 0 || Double.isNaN(speed)) throw new IllegalArgumentException("Speed cannot be negative or NaN: " + speed + ", flags:" + BitUtil.LITTLE.toBitString(flags)); if (speed < speedEncoder.factor / 2) return setLowSpeed(flags, speed, false); if (speed > getMaxSpeed()) speed = getMaxSpeed(); return speedEncoder.setDoubleValue(flags, speed); } protected long setLowSpeed( long flags, double speed, boolean reverse ) { return setAccess(speedEncoder.setDoubleValue(flags, 0), false, false); } @Override public double getSpeed( long flags ) { double speedVal = speedEncoder.getDoubleValue(flags); if (speedVal < 0) throw new IllegalStateException("Speed was negative!? " + speedVal); return speedVal; } @Override public long setReverseSpeed( long flags, double speed ) { return setSpeed(flags, speed); } @Override public double getReverseSpeed( long flags ) { return getSpeed(flags); } @Override public long setProperties( double speed, boolean forward, boolean backward ) { return setAccess(setSpeed(0, speed), forward, backward); } @Override public double getMaxSpeed() { return speedEncoder.getMaxValue(); } /** * @return -1 if no maxspeed found */ protected double getMaxSpeed( OSMWay way ) { double maxSpeed = parseSpeed(way.getTag("maxspeed")); double fwdSpeed = parseSpeed(way.getTag("maxspeed:forward")); if (fwdSpeed >= 0 && (maxSpeed < 0 || fwdSpeed < maxSpeed)) maxSpeed = fwdSpeed; double backSpeed = parseSpeed(way.getTag("maxspeed:backward")); if (backSpeed >= 0 && (maxSpeed < 0 || backSpeed < maxSpeed)) maxSpeed = backSpeed; return maxSpeed; } @Override public int hashCode() { int hash = 7; hash = 61 * hash + (int) this.directionBitMask; hash = 61 * hash + this.toString().hashCode(); return hash; } @Override public boolean equals( Object obj ) { if (obj == null) return false; // only rely on the string // if (getClass() != obj.getClass()) // return false; final AbstractFlagEncoder other = (AbstractFlagEncoder) obj; if (this.directionBitMask != other.directionBitMask) return false; return this.toString().equals(other.toString()); } /** * @return the speed in km/h */ protected double parseSpeed( String str ) { if (Helper.isEmpty(str)) return -1; // on some German autobahns and a very few other places if ("none".equals(str)) return 140; if (str.endsWith(":rural") || str.endsWith(":trunk")) return 80; if (str.endsWith(":urban")) return 50; if (str.equals("walk") || str.endsWith(":living_street")) return 6; try { int val; // see https://en.wikipedia.org/wiki/Knot_%28unit%29#Definitions int mpInteger = str.indexOf("mp"); if (mpInteger > 0) { str = str.substring(0, mpInteger).trim(); val = Integer.parseInt(str); return val * DistanceCalcEarth.KM_MILE; } int knotInteger = str.indexOf("knots"); if (knotInteger > 0) { str = str.substring(0, knotInteger).trim(); val = Integer.parseInt(str); return val * 1.852; } int kmInteger = str.indexOf("km"); if (kmInteger > 0) { str = str.substring(0, kmInteger).trim(); } else { kmInteger = str.indexOf("kph"); if (kmInteger > 0) { str = str.substring(0, kmInteger).trim(); } } return Integer.parseInt(str); } catch (Exception ex) { return -1; } } /** * Second parsing step. Invoked after splitting the edges. Currently used to offer a hook to * calculate precise speed values based on elevation data stored in the specified edge. */ public void applyWayTags( OSMWay way, EdgeIteratorState edge ) { } /** * Special handling for ferry ways. */ protected long handleFerryTags( OSMWay way, double unknownSpeed, double shortTripsSpeed, double longTripsSpeed ) { long duration = 0; try { // During the reader process we have converted the duration value into a artificial tag called "duration:seconds". duration = Long.parseLong(way.getTag("duration:seconds")); } catch (Exception ex) { } // seconds to hours double durationInHours = duration / 60d / 60d; if (durationInHours > 0) try { // Check if our graphhopper specific artificially created estimated_distance way tag is present Number estimatedLength = way.getTag("estimated_distance", null); if (estimatedLength != null) { // to km double val = estimatedLength.doubleValue() / 1000; // If duration AND distance is available we can calculate the speed more precisely // and set both speed to the same value. Factor 1.4 slower because of waiting time! double calculatedTripSpeed = val / durationInHours / 1.4; // Plausibility check especially for the case of wrongly used PxM format with the intention to // specify the duration in minutes, but actually using months if (calculatedTripSpeed > 0.01d) { // If we have a very short ferry with an average lower compared to what we can encode // then we need to avoid setting it as otherwise the edge would not be found at all any more. if (Math.round(calculatedTripSpeed) > speedEncoder.factor / 2) { shortTripsSpeed = Math.round(calculatedTripSpeed); if (shortTripsSpeed > getMaxSpeed()) shortTripsSpeed = getMaxSpeed(); longTripsSpeed = shortTripsSpeed; } else { // Now we set to the lowest possible still accessible speed. shortTripsSpeed = speedEncoder.factor / 2; } } else { logger.warn("Unrealistic long duration ignored in way with OSMID=" + way.getId() + " : Duration tag value=" + way.getTag("duration") + " (=" + Math.round(duration / 60d) + " minutes)"); durationInHours = 0; } } } catch (Exception ex) { } if (durationInHours == 0) { // unknown speed -> put penalty on ferry transport return setSpeed(0, unknownSpeed); } else if (durationInHours > 1) { // lengthy ferries should be faster than short trip ferry return setSpeed(0, longTripsSpeed); } else { return setSpeed(0, shortTripsSpeed); } } void setWayBitMask( int usedBits, int shift ) { wayBitMask = (1L << usedBits) - 1; wayBitMask <<= shift; } long getWayBitMask() { return wayBitMask; } void setRelBitMask( int usedBits, int shift ) { relBitMask = (1L << usedBits) - 1; relBitMask <<= shift; } long getRelBitMask() { return relBitMask; } void setNodeBitMask( int usedBits, int shift ) { nodeBitMask = (1L << usedBits) - 1; nodeBitMask <<= shift; } long getNodeBitMask() { return nodeBitMask; } /** * Defines the bits reserved for storing turn restriction and turn cost *

* @param shift bit offset for the first bit used by this encoder * @return incremented shift value pointing behind the last used bit */ public int defineTurnBits( int index, int shift ) { if (maxTurnCosts == 0) return shift; // optimization for turn restrictions only else if (maxTurnCosts == 1) { turnRestrictionBit = 1L << shift; return shift + 1; } int turnBits = Helper.countBitValue(maxTurnCosts); turnCostEncoder = new EncodedValue("TurnCost", shift, turnBits, 1, 0, maxTurnCosts) { // override to avoid expensive Math.round @Override public final long getValue( long flags ) { // find value flags &= mask; flags >>>= shift; return flags; } }; return shift + turnBits; } @Override public boolean isTurnRestricted( long flags ) { if (maxTurnCosts == 0) return false; else if (maxTurnCosts == 1) return (flags & turnRestrictionBit) != 0; return turnCostEncoder.getValue(flags) == maxTurnCosts; } @Override public double getTurnCost( long flags ) { if (maxTurnCosts == 0) return 0; else if (maxTurnCosts == 1) return ((flags & turnRestrictionBit) == 0) ? 0 : Double.POSITIVE_INFINITY; long cost = turnCostEncoder.getValue(flags); if (cost == maxTurnCosts) return Double.POSITIVE_INFINITY; return cost; } @Override public long getTurnFlags( boolean restricted, double costs ) { if (maxTurnCosts == 0) return 0; else if (maxTurnCosts == 1) { if (costs != 0) throw new IllegalArgumentException("Only restrictions are supported"); return restricted ? turnRestrictionBit : 0; } if (restricted) { if (costs != 0 || Double.isInfinite(costs)) throw new IllegalArgumentException("Restricted turn can only have infinite costs (or use 0)"); } else { if (costs >= maxTurnCosts) throw new IllegalArgumentException("Cost is too high. Or specifiy restricted == true"); } if (costs < 0) throw new IllegalArgumentException("Turn costs cannot be negative"); if (costs >= maxTurnCosts || restricted) costs = maxTurnCosts; return turnCostEncoder.setValue(0L, (int) costs); } protected boolean isFerry( long internalFlags ) { return (internalFlags & ferryBit) != 0; } protected boolean isAccept( long internalFlags ) { return (internalFlags & acceptBit) != 0; } @Override public boolean isBackward( long flags ) { return (flags & backwardBit) != 0; } @Override public boolean isForward( long flags ) { return (flags & forwardBit) != 0; } @Override public long setBool( long flags, int key, boolean value ) { switch (key) { case K_FORWARD: return value ? flags | forwardBit : flags & ~forwardBit; case K_BACKWARD: return value ? flags | backwardBit : flags & ~backwardBit; case K_ROUNDABOUT: return value ? flags | roundaboutBit : flags & ~roundaboutBit; default: throw new IllegalArgumentException("Unknown key " + key + " for boolean value"); } } @Override public boolean isBool( long flags, int key ) { switch (key) { case K_FORWARD: return isForward(flags); case K_BACKWARD: return isBackward(flags); case K_ROUNDABOUT: return (flags & roundaboutBit) != 0; default: throw new IllegalArgumentException("Unknown key " + key + " for boolean value"); } } @Override public long setLong( long flags, int key, long value ) { throw new UnsupportedOperationException("Unknown key " + key + " for long value."); } @Override public long getLong( long flags, int key ) { throw new UnsupportedOperationException("Unknown key " + key + " for long value."); } @Override public long setDouble( long flags, int key, double value ) { throw new UnsupportedOperationException("Unknown key " + key + " for double value."); } @Override public double getDouble( long flags, int key ) { throw new UnsupportedOperationException("Unknown key " + key + " for double value."); } /** * @param way: needed to retrieve OSM tags * @param speed: speed guessed e.g. from the road type or other tags * @return The assumed speed. */ protected double applyMaxSpeed( OSMWay way, double speed ) { double maxSpeed = getMaxSpeed(way); // We obay speed limits if (maxSpeed >= 0) { // We assume that the average speed is 90% of the allowed maximum return maxSpeed * 0.9; } return speed; } protected String getPropertiesString() { return "speedFactor=" + speedFactor + "|speedBits=" + speedBits + "|turnCosts=" + (maxTurnCosts > 0); } @Override public boolean supports( Class feature ) { if (TurnWeighting.class.isAssignableFrom(feature)) return maxTurnCosts > 0; return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy