/* * 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.util.Helper; import com.graphhopper.util.PMap; import java.util.*; /** * Defines bit layout for cars. (speed, access, ferries, ...) * <p> * * @author Peter Karich * @author Nop */ public class CarFlagEncoder extends AbstractFlagEncoder { protected final Map<String, Integer> trackTypeSpeedMap = new HashMap<String, Integer>(); protected final Set<String> badSurfaceSpeedMap = new HashSet<String>(); // This value determines the maximal possible on roads with bad surfaces protected int badSurfaceSpeed; // This value determines the speed for roads with access=destination protected int destinationSpeed; /** * A map which associates string to speed. Get some impression: * http://www.itoworld.com/map/124#fullscreen * http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Maxspeed */ protected final Map<String, Integer> defaultSpeedMap = new HashMap<String, Integer>(); public CarFlagEncoder() { this(5, 5, 0); } public CarFlagEncoder(PMap properties) { this((int) properties.getLong("speed_bits", 5), properties.getDouble("speed_factor", 5), properties.getBool("turn_costs", false) ? 1 : 0); this.properties = properties; this.setBlockFords(properties.getBool("block_fords", true)); this.setBlockByDefault(properties.getBool("block_barriers", true)); } public CarFlagEncoder(String propertiesStr) { this(new PMap(propertiesStr)); } public CarFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts) { super(speedBits, speedFactor, maxTurnCosts); restrictions.addAll(Arrays.asList("motorcar", "motor_vehicle", "vehicle", "access")); restrictedValues.add("private"); restrictedValues.add("agricultural"); restrictedValues.add("forestry"); restrictedValues.add("no"); restrictedValues.add("restricted"); restrictedValues.add("delivery"); restrictedValues.add("military"); restrictedValues.add("emergency"); intendedValues.add("yes"); intendedValues.add("permissive"); potentialBarriers.add("gate"); potentialBarriers.add("lift_gate"); potentialBarriers.add("kissing_gate"); potentialBarriers.add("swing_gate"); absoluteBarriers.add("bollard"); absoluteBarriers.add("stile"); absoluteBarriers.add("turnstile"); absoluteBarriers.add("cycle_barrier"); absoluteBarriers.add("motorcycle_barrier"); absoluteBarriers.add("block"); absoluteBarriers.add("bus_trap"); absoluteBarriers.add("sump_buster"); trackTypeSpeedMap.put("grade1", 20); // paved trackTypeSpeedMap.put("grade2", 15); // now unpaved - gravel mixed with ... trackTypeSpeedMap.put("grade3", 10); // ... hard and soft materials badSurfaceSpeedMap.add("cobblestone"); badSurfaceSpeedMap.add("grass_paver"); badSurfaceSpeedMap.add("gravel"); badSurfaceSpeedMap.add("sand"); badSurfaceSpeedMap.add("paving_stones"); badSurfaceSpeedMap.add("dirt"); badSurfaceSpeedMap.add("ground"); badSurfaceSpeedMap.add("grass"); badSurfaceSpeedMap.add("unpaved"); badSurfaceSpeedMap.add("compacted"); // limit speed on bad surfaces to 30 km/h badSurfaceSpeed = 30; destinationSpeed = 5; maxPossibleSpeed = 140; // autobahn defaultSpeedMap.put("motorway", 100); defaultSpeedMap.put("motorway_link", 70); defaultSpeedMap.put("motorroad", 90); // bundesstraße defaultSpeedMap.put("trunk", 70); defaultSpeedMap.put("trunk_link", 65); // linking bigger town defaultSpeedMap.put("primary", 65); defaultSpeedMap.put("primary_link", 60); // linking towns + villages defaultSpeedMap.put("secondary", 60); defaultSpeedMap.put("secondary_link", 50); // streets without middle line separation defaultSpeedMap.put("tertiary", 50); defaultSpeedMap.put("tertiary_link", 40); defaultSpeedMap.put("unclassified", 30); defaultSpeedMap.put("residential", 30); // spielstraße defaultSpeedMap.put("living_street", 5); defaultSpeedMap.put("service", 20); // unknown road defaultSpeedMap.put("road", 20); // forestry stuff defaultSpeedMap.put("track", 15); init(); } @Override public int getVersion() { return 1; } /** * Define the place of the speedBits in the edge flags for car. */ @Override public int defineWayBits(int index, int shift) { // first two bits are reserved for route handling in superclass shift = super.defineWayBits(index, shift); speedEncoder = new EncodedDoubleValue("Speed", shift, speedBits, speedFactor, defaultSpeedMap.get("secondary"), maxPossibleSpeed); return shift + speedEncoder.getBits(); } protected double getSpeed(ReaderWay way) { String highwayValue = way.getTag("highway"); if (!Helper.isEmpty(highwayValue) && way.hasTag("motorroad", "yes") && highwayValue != "motorway" && highwayValue != "motorway_link") { highwayValue = "motorroad"; } Integer speed = defaultSpeedMap.get(highwayValue); if (speed == null) throw new IllegalStateException(toString() + ", no speed found for: " + highwayValue + ", tags: " + way); if (highwayValue.equals("track")) { String tt = way.getTag("tracktype"); if (!Helper.isEmpty(tt)) { Integer tInt = trackTypeSpeedMap.get(tt); if (tInt != null) speed = tInt; } } return speed; } @Override public long acceptWay(ReaderWay way) { // TODO: Ferries have conditionals, like opening hours or are closed during some time in the year String highwayValue = way.getTag("highway"); if (highwayValue == null) { if (way.hasTag("route", ferries)) { String motorcarTag = way.getTag("motorcar"); if (motorcarTag == null) motorcarTag = way.getTag("motor_vehicle"); if (motorcarTag == null && !way.hasTag("foot") && !way.hasTag("bicycle") || "yes".equals(motorcarTag)) return acceptBit | ferryBit; } return 0; } if ("track".equals(highwayValue)) { String tt = way.getTag("tracktype"); if (tt != null && !tt.equals("grade1") && !tt.equals("grade2") && !tt.equals("grade3")) return 0; } if (!defaultSpeedMap.containsKey(highwayValue)) return 0; if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable")) return 0; // multiple restrictions needs special handling compared to foot and bike, see also motorcycle String firstValue = way.getFirstPriorityTag(restrictions); if (!firstValue.isEmpty()) { if (restrictedValues.contains(firstValue) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) return 0; if (intendedValues.contains(firstValue)) return acceptBit; } // do not drive street cars into fords if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) return 0; if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way)) return 0; else return acceptBit; } @Override public long handleRelationTags(ReaderRelation relation, long oldRelationFlags) { return oldRelationFlags; } @Override public long handleWayTags(ReaderWay way, long allowed, long relationFlags) { if (!isAccept(allowed)) return 0; long flags = 0; if (!isFerry(allowed)) { // get assumed speed from highway type double speed = getSpeed(way); speed = applyMaxSpeed(way, speed); speed = applyBadSurfaceSpeed(way, speed); flags = setSpeed(flags, speed); boolean isRoundabout = way.hasTag("junction", "roundabout"); if (isRoundabout) flags = setBool(flags, K_ROUNDABOUT, true); if (isOneway(way) || isRoundabout) { if (isBackwardOneway(way)) flags |= backwardBit; if (isForwardOneway(way)) flags |= forwardBit; } else flags |= directionBitMask; } else { double ferrySpeed = getFerrySpeed(way, defaultSpeedMap.get("living_street"), defaultSpeedMap.get("service"), defaultSpeedMap.get("residential")); flags = setSpeed(flags, ferrySpeed); flags |= directionBitMask; } for (String restriction : restrictions) { if (way.hasTag(restriction, "destination")) { // This is problematic as Speed != Time flags = this.speedEncoder.setDoubleValue(flags, destinationSpeed); } } return flags; } /** * make sure that isOneway is called before */ protected boolean isBackwardOneway(ReaderWay way) { return way.hasTag("oneway", "-1") || way.hasTag("vehicle:forward", "no") || way.hasTag("motor_vehicle:forward", "no"); } /** * make sure that isOneway is called before */ protected boolean isForwardOneway(ReaderWay way) { return !way.hasTag("oneway", "-1") && !way.hasTag("vehicle:forward", "no") && !way.hasTag("motor_vehicle:forward", "no"); } protected boolean isOneway(ReaderWay way) { return way.hasTag("oneway", oneways) || way.hasTag("vehicle:backward") || way.hasTag("vehicle:forward") || way.hasTag("motor_vehicle:backward") || way.hasTag("motor_vehicle:forward"); } public String getWayInfo(ReaderWay way) { String str = ""; String highwayValue = way.getTag("highway"); // for now only motorway links if ("motorway_link".equals(highwayValue)) { String destination = way.getTag("destination"); if (!Helper.isEmpty(destination)) { int counter = 0; for (String d : destination.split(";")) { if (d.trim().isEmpty()) continue; if (counter > 0) str += ", "; str += d.trim(); counter++; } } } if (str.isEmpty()) return str; // I18N if (str.contains(",")) return "destinations: " + str; else return "destination: " + str; } /** * @param way: needed to retrieve tags * @param speed: speed guessed e.g. from the road type or other tags * @return The assumed speed */ protected double applyBadSurfaceSpeed(ReaderWay way, double speed) { // limit speed if bad surface if (badSurfaceSpeed > 0 && speed > badSurfaceSpeed && way.hasTag("surface", badSurfaceSpeedMap)) speed = badSurfaceSpeed; return speed; } @Override public String toString() { return "car"; } }