Repository: graphhopper/graphhopper
Pull request #1683 (2019-08-08 07:19:31)

Accept turn relation
To enable the _flagEncoders_ to accept or not a **turn relation**.

For example, a _busFlagEncoder_ does not take into account the following turn relation, whereas the _carFlagEncoder_ does:
type=restriction
restriction=no_left_turn
**except**=bus

And a _busFlagEncoder_ wants to respect the following turn relation dedicated to bus, whereas _carFlagEncoder_ doesn't:
type=restriction
**restriction:bus**=no_left_turn


karussell: Thx, we'll take care of this (in a few weeks)
easbar: Would you mind updating this to recent master ? The problem why the build failed should now be fixed with #1671.
easbar: Did you sign the contributor agreement yet ? https://github.com/graphhopper/graphhopper/blob/master/CONTRIBUTING.md#license-agreement

Patch:
diff --git a/core/files/changelog.txt b/core/files/changelog.txt
index c14768100..176fba9e6 100644
--- a/core/files/changelog.txt
+++ b/core/files/changelog.txt
@@ -1,3 +1,6 @@
+0.14
+    conditional turn restrictions now supported, #1683
+
 0.13
     removed docker compose file
     PathDetails return null instead of -1 for Infinity by default
diff --git a/core/src/main/java/com/graphhopper/reader/OSMTurnRelation.java b/core/src/main/java/com/graphhopper/reader/OSMTurnRelation.java
new file mode 100644
index 000000000..8ccdd1b33
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/reader/OSMTurnRelation.java
@@ -0,0 +1,140 @@
+/*
+ *  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;
+
+import java.util.*;
+
+/**
+ * Helper object which gives node cost entries for a given OSM-relation of type "restriction"
+ * <p>
+ *
+ * @author Karl Hübner
+ */
+public class OSMTurnRelation {
+    private final long fromOsmWayId;
+    private final long viaOsmNodeId;
+    private final long toOsmWayId;
+    private final Type restriction;
+    // vehicleTypeRestricted contains the dedicated vehicle type
+    // example: restriction:bus = no_left_turn => vehicleTypeRestricted = "bus";
+    private String vehicleTypeRestricted;
+    private List<String> vehicleTypesExcept;
+
+    public OSMTurnRelation(long fromWayID, long viaNodeID, long toWayID, Type restrictionType) {
+        this.fromOsmWayId = fromWayID;
+        this.viaOsmNodeId = viaNodeID;
+        this.toOsmWayId = toWayID;
+        this.restriction = restrictionType;
+        this.vehicleTypeRestricted = "";
+        this.vehicleTypesExcept = new ArrayList<>();
+    }
+
+    public long getOsmIdFrom() {
+        return fromOsmWayId;
+    }
+
+    public long getOsmIdTo() {
+        return toOsmWayId;
+    }
+
+    public long getViaOsmNodeId() {
+        return viaOsmNodeId;
+    }
+
+    public Type getRestriction() {
+        return restriction;
+    }
+
+    public String getVehicleTypeRestricted() {
+        return vehicleTypeRestricted;
+    }
+
+    public void setVehicleTypeRestricted(String vehicleTypeRestricted) {
+        this.vehicleTypeRestricted = vehicleTypeRestricted;
+    }
+
+    public List<String> getVehicleTypesExcept() {
+        return vehicleTypesExcept;
+    }
+
+    public void addVehicleTypesExcept(List<String> vehicleTypesExcept) {
+        this.vehicleTypesExcept = vehicleTypesExcept;
+    }
+
+    /**
+     * For a conditional turn restriction, test each vehicle type to verify if it is concerned.
+     * For a normal turn restriction (non conditional), the restriction is necessary considered.
+     * @param vehicleTypes
+     * @return
+     */
+    public boolean isVehicleTypeConcernedByTurnRestriction(List<String> vehicleTypes) {
+        if (vehicleTypeRestricted.isEmpty() && vehicleTypesExcept.isEmpty()) {
+            //normal turn restriction, not dedicated to a vehicle type
+            return true;
+        }
+        for (String vehicleType : vehicleTypes) {
+            if (!vehicleTypeRestricted.isEmpty()) {
+                if (vehicleTypeRestricted.equals(vehicleType)) {
+                    //turn restriction dedicated to vehicleType, for example: restriction:bus = no_left_turn
+                    return true;
+                }
+            }
+            if (!vehicleTypesExcept.isEmpty()) {
+                if (vehicleTypesExcept.contains(vehicleType)) {
+                    //turn restriction except for vehicleType
+                    return false;
+                }
+            }
+        }
+        if (!vehicleTypeRestricted.isEmpty()) {
+            //turn restriction dedicated to a vehicle type not found in vehicleTypes
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "*-(" + fromOsmWayId + ")->" + viaOsmNodeId + "-(" + toOsmWayId + ")->*";
+    }
+
+    public enum Type {
+        UNSUPPORTED, NOT, ONLY;
+
+        private static final Map<String, Type> tags = new HashMap<>();
+
+        static {
+            tags.put("no_left_turn", NOT);
+            tags.put("no_right_turn", NOT);
+            tags.put("no_straight_on", NOT);
+            tags.put("no_u_turn", NOT);
+            tags.put("only_right_turn", ONLY);
+            tags.put("only_left_turn", ONLY);
+            tags.put("only_straight_on", ONLY);
+        }
+
+        public static Type getRestrictionType(String tag) {
+            Type result = null;
+            if (tag != null) {
+                result = tags.get(tag);
+            }
+            return (result != null) ? result : UNSUPPORTED;
+        }
+    }
+
+}
diff --git a/core/src/main/java/com/graphhopper/reader/ReaderElement.java b/core/src/main/java/com/graphhopper/reader/ReaderElement.java
index 81dc5aa19..50ca7ac13 100644
--- a/core/src/main/java/com/graphhopper/reader/ReaderElement.java
+++ b/core/src/main/java/com/graphhopper/reader/ReaderElement.java
@@ -17,6 +17,8 @@
  */
 package com.graphhopper.reader;
 
+import com.graphhopper.util.Helper;
+
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -95,6 +97,24 @@ public abstract class ReaderElement {
         return val;
     }
 
+    public String getKeyStartsWith(String keyPrefix) {
+        int nbMatchedKeys = 0;
+        String matchedKey = null;
+        for (String key : properties.keySet()) {
+            if (key.startsWith(keyPrefix)) {
+                nbMatchedKeys++;
+                matchedKey = key;
+                if (nbMatchedKeys > 1) {
+                    throw new IllegalArgumentException("Several matched keys found for keyPrefix " + keyPrefix);
+                }
+            }
+        }
+        if (nbMatchedKeys == 1) {
+            return matchedKey;
+        }
+        return null;
+    }
+
     public void setTag(String name, Object value) {
         properties.put(name, value);
     }
@@ -145,6 +165,15 @@ public abstract class ReaderElement {
         return false;
     }
 
+    public boolean hasTagStartsWith(String keyPrefix) {
+        for (String key : properties.keySet()) {
+            if (key.startsWith(keyPrefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Returns the first existing tag of the specified list where the order is important.
      */
diff --git a/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
index 6fa3f8745..863ffae5b 100644
--- a/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/AbstractFlagEncoder.java
@@ -17,10 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
-import com.graphhopper.reader.ConditionalTagInspector;
-import com.graphhopper.reader.ReaderNode;
-import com.graphhopper.reader.ReaderRelation;
-import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.reader.*;
 import com.graphhopper.reader.osm.conditional.ConditionalOSMTagInspector;
 import com.graphhopper.reader.osm.conditional.DateRangeParser;
 import com.graphhopper.routing.profiles.*;
@@ -181,6 +178,8 @@ public abstract class AbstractFlagEncoder implements FlagEncoder {
         return shift;
     }
 
+    public boolean acceptsTurnRelation(OSMTurnRelation relation) { return true; }
+
     /**
      * 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.
diff --git a/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java
index 34ec01344..49f73f583 100644
--- a/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/CarFlagEncoder.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.reader.ReaderRelation;
 import com.graphhopper.reader.ReaderWay;
 import com.graphhopper.routing.profiles.EncodedValue;
@@ -186,6 +187,14 @@ public class CarFlagEncoder extends AbstractFlagEncoder {
         return speed;
     }
 
+    @Override
+    public boolean acceptsTurnRelation(OSMTurnRelation relation) {
+        if (relation.isVehicleTypeConcernedByTurnRestriction(restrictions)) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public EncodingManager.Access getAccess(ReaderWay way) {
         // TODO: Ferries have conditionals, like opening hours or are closed during some time in the year
diff --git a/core/src/main/java/com/graphhopper/routing/util/DataFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/DataFlagEncoder.java
index 5d5ce9784..e7b36598d 100644
--- a/core/src/main/java/com/graphhopper/routing/util/DataFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/DataFlagEncoder.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.reader.ReaderRelation;
 import com.graphhopper.reader.ReaderWay;
 import com.graphhopper.routing.profiles.*;
@@ -115,6 +116,14 @@ public class DataFlagEncoder extends AbstractFlagEncoder {
         return oldRelationFlags;
     }
 
+    @Override
+    public boolean acceptsTurnRelation(OSMTurnRelation relation) {
+        if (relation.isVehicleTypeConcernedByTurnRestriction(restrictions)) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public EncodingManager.Access getAccess(ReaderWay way) {
         // important to skip unsupported highways, otherwise too many have to be removed after graph creation
diff --git a/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java b/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java
index e74c20362..f22885e73 100644
--- a/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java
+++ b/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.reader.ReaderNode;
 import com.graphhopper.reader.ReaderRelation;
 import com.graphhopper.reader.ReaderWay;
diff --git a/core/src/main/java/com/graphhopper/routing/util/FlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/FlagEncoder.java
index 3b56c8f19..c5caf46f0 100644
--- a/core/src/main/java/com/graphhopper/routing/util/FlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/FlagEncoder.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
 import com.graphhopper.routing.profiles.EncodedValueLookup;
@@ -53,6 +54,12 @@ public interface FlagEncoder extends TurnCostEncoder, EncodedValueLookup {
      */
     DecimalEncodedValue getAverageSpeedEnc();
 
+    /**
+     * @return whether or not this encoder should apply the given turn restriction
+     * @see OSMTurnRelation
+     */
+    boolean acceptsTurnRelation(OSMTurnRelation relation);
+
     /**
      * Returns true if the feature class is supported like TurnWeighting or PriorityWeighting.
      * Use support(String) instead.
diff --git a/core/src/main/java/com/graphhopper/routing/util/FootFlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/FootFlagEncoder.java
index 725224286..71d5869b3 100644
--- a/core/src/main/java/com/graphhopper/routing/util/FootFlagEncoder.java
+++ b/core/src/main/java/com/graphhopper/routing/util/FootFlagEncoder.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.reader.ReaderRelation;
 import com.graphhopper.reader.ReaderWay;
 import com.graphhopper.routing.profiles.DecimalEncodedValue;
@@ -196,6 +197,11 @@ public class FootFlagEncoder extends AbstractFlagEncoder {
         return 0;
     }
 
+    @Override
+    public boolean acceptsTurnRelation(OSMTurnRelation relation) {
+        return false;
+    }
+
     /**
      * Some ways are okay but not separate for pedestrians.
      */
diff --git a/docs/core/turn-restrictions.md b/docs/core/turn-restrictions.md
index 00cd8544a..94b1eccf1 100644
--- a/docs/core/turn-restrictions.md
+++ b/docs/core/turn-restrictions.md
@@ -28,4 +28,17 @@ profiles with different u-turn costs). To disable the 'speed mode' per request y
 the value of `u_turn_costs` freely.
 
 While OSM data only contains turn *restrictions*, the GraphHopper routing engine can also deal with turn *costs*, i.e.
-you can specify custom turn costs for each turn at each junction.
\ No newline at end of file
+you can specify custom turn costs for each turn at each junction.
+
+Conditional turn restriction are supported. For example, the following no left turn restriction concerns only bus :
+
+> type=restriction  
+> restriction:bus=no_left_turn
+
+Another example, using the *except* tag, means only *bicycle* are allowed to turn left:
+
+> type=restriction  
+> restriction=no_left_turn  
+> except=bicycle
+
+You can overwrite `FlagEncoder#acceptsTurnRelation` to change the default handling of turn restrictions in your customized vehicle profile.
\ No newline at end of file
diff --git a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/PtFlagEncoder.java b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/PtFlagEncoder.java
index 56200346d..12b8b52be 100644
--- a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/PtFlagEncoder.java
+++ b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/PtFlagEncoder.java
@@ -18,6 +18,7 @@
 
 package com.graphhopper.reader.gtfs;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.reader.ReaderRelation;
 import com.graphhopper.reader.ReaderWay;
 import com.graphhopper.routing.profiles.EncodedValue;
@@ -57,6 +58,11 @@ public class PtFlagEncoder extends AbstractFlagEncoder {
         return oldRelationFlags;
     }
 
+    @Override
+    public boolean acceptsTurnRelation(OSMTurnRelation relation) {
+        return false;
+    }
+
     @Override
     public EncodingManager.Access getAccess(ReaderWay way) {
         return EncodingManager.Access.CAN_SKIP;
diff --git a/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java b/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java
index e865a2a0d..fee1d8ddb 100644
--- a/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java
+++ b/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMReader.java
@@ -23,11 +23,11 @@ import com.graphhopper.coll.LongIntMap;
 import com.graphhopper.reader.*;
 import com.graphhopper.reader.dem.ElevationProvider;
 import com.graphhopper.reader.dem.GraphElevationSmoothing;
-import com.graphhopper.reader.osm.OSMTurnRelation.TurnCostTableEntry;
 import com.graphhopper.routing.profiles.BooleanEncodedValue;
 import com.graphhopper.routing.util.DefaultEdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.routing.util.TurnCostEncoder;
 import com.graphhopper.routing.weighting.TurnWeighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.*;
@@ -181,8 +181,9 @@ public class OSMReader implements DataReader {
                     if (!relation.isMetaRelation() && relation.hasTag("type", "route"))
                         prepareWaysWithRelationInfo(relation);
 
-                    if (relation.hasTag("type", "restriction"))
+                    if (relation.hasTag("type", "restriction")) {
                         prepareRestrictionRelation(relation);
+                    }
 
                     if (++tmpRelationCounter % 100_000 == 0) {
                         LOGGER.info(nf(tmpRelationCounter) + " (preprocess), osmWayMap:" + nf(getRelFlagsMap().size())
@@ -429,13 +430,15 @@ public class OSMReader implements DataReader {
         Map<Long, TurnCostTableEntry> entries = new LinkedHashMap<>();
 
         for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
-            for (TurnCostTableEntry entry : analyzeTurnRelation(encoder, turnRelation)) {
-                TurnCostTableEntry oldEntry = entries.get(entry.getItemId());
-                if (oldEntry != null) {
-                    // merging different encoders
-                    oldEntry.flags |= entry.flags;
-                } else {
-                    entries.put(entry.getItemId(), entry);
+            if (encoder.acceptsTurnRelation(turnRelation)) {
+                for (TurnCostTableEntry entry : analyzeTurnRelation(encoder, turnRelation)) {
+                    TurnCostTableEntry oldEntry = entries.get(entry.getItemId());
+                    if (oldEntry != null) {
+                        // merging different encoders
+                        oldEntry.flags |= entry.flags;
+                    } else {
+                        entries.put(entry.getItemId(), entry);
+                    }
                 }
             }
         }
@@ -457,7 +460,7 @@ public class OSMReader implements DataReader {
             edgeInExplorer = graph.createEdgeExplorer(DefaultEdgeFilter.inEdges(encoder));
             inExplorerMap.put(encoder, edgeInExplorer);
         }
-        return turnRelation.getRestrictionAsEntries(encoder, edgeOutExplorer, edgeInExplorer, this);
+        return getRestrictionAsEntries(turnRelation, encoder, edgeOutExplorer, edgeInExplorer, this);
     }
 
     /**
@@ -859,7 +862,25 @@ public class OSMReader implements DataReader {
      * @return the OSM turn relation, <code>null</code>, if unsupported turn relation
      */
     OSMTurnRelation createTurnRelation(ReaderRelation relation) {
-        OSMTurnRelation.Type type = OSMTurnRelation.Type.getRestrictionType(relation.getTag("restriction"));
+        String tagRestriction = "";
+        String vehicleTypeRestricted = "";
+        List<String> vehicleTypesExcept = new ArrayList<>();
+        if (relation.hasTag("restriction")) {
+            tagRestriction = relation.getTag("restriction");
+        } else if (relation.hasTagStartsWith("restriction:")) {
+            vehicleTypeRestricted = relation.getKeyStartsWith("restriction:");
+            tagRestriction = relation.getTag(vehicleTypeRestricted);
+            vehicleTypeRestricted = vehicleTypeRestricted.replace("restriction:", "").trim();
+        }
+        if (relation.hasTag("except")) {
+            String tagExcept = relation.getTag("except");
+            if (!Helper.isEmpty(tagExcept)) {
+                List<String> vehicleTypes = new ArrayList<>(Arrays.asList(tagExcept.split(";")));
+                for (String vehicleType : vehicleTypes)
+                    vehicleTypesExcept.add(vehicleType.trim());
+            }
+        }
+        OSMTurnRelation.Type type = OSMTurnRelation.Type.getRestrictionType(tagRestriction);
         if (type != OSMTurnRelation.Type.UNSUPPORTED) {
             long fromWayID = -1;
             long viaNodeID = -1;
@@ -877,12 +898,97 @@ public class OSMReader implements DataReader {
                 }
             }
             if (fromWayID >= 0 && toWayID >= 0 && viaNodeID >= 0) {
-                return new OSMTurnRelation(fromWayID, viaNodeID, toWayID, type);
+                OSMTurnRelation osmTurnRelation = new OSMTurnRelation(fromWayID, viaNodeID, toWayID, type);
+                osmTurnRelation.setVehicleTypeRestricted(vehicleTypeRestricted);
+                osmTurnRelation.addVehicleTypesExcept(vehicleTypesExcept);
+                return osmTurnRelation;
             }
         }
         return null;
     }
 
+    /**
+     * Transforms this relation into a collection of turn cost entries
+     * <p>
+     *
+     * @param edgeOutExplorer an edge filter which only allows outgoing edges
+     * @param edgeInExplorer  an edge filter which only allows incoming edges
+     * @return a collection of node cost entries which can be added to the graph later
+     */
+    public static Collection<TurnCostTableEntry> getRestrictionAsEntries(OSMTurnRelation osmTurnRelation, TurnCostEncoder encoder,
+                                                                  EdgeExplorer edgeOutExplorer, EdgeExplorer edgeInExplorer, OSMReader osmReader) {
+        int nodeVia = osmReader.getInternalNodeIdOfOsmNode(osmTurnRelation.getViaOsmNodeId());
+
+        try {
+            // street with restriction was not included (access or tag limits etc)
+            if (nodeVia == OSMReader.EMPTY_NODE)
+                return Collections.emptyList();
+
+            int edgeIdFrom = EdgeIterator.NO_EDGE;
+
+            // get all incoming edges and receive the edge which is defined by fromOsm
+            EdgeIterator iter = edgeInExplorer.setBaseNode(nodeVia);
+
+            while (iter.next()) {
+                if (osmReader.getOsmIdOfInternalEdge(iter.getEdge()) == osmTurnRelation.getOsmIdFrom()) {
+                    edgeIdFrom = iter.getEdge();
+                    break;
+                }
+            }
+
+            if (!EdgeIterator.Edge.isValid(edgeIdFrom))
+                return Collections.emptyList();
+
+            final Collection<TurnCostTableEntry> entries = new ArrayList<>();
+            // get all outgoing edges of the via node
+            iter = edgeOutExplorer.setBaseNode(nodeVia);
+            // for TYPE_ONLY_* we add ALL restrictions (from, via, * ) EXCEPT the given turn
+            // for TYPE_NOT_*  we add ONE restriction  (from, via, to)
+            while (iter.next()) {
+                int edgeId = iter.getEdge();
+                long wayId = osmReader.getOsmIdOfInternalEdge(edgeId);
+                if (edgeId != edgeIdFrom && osmTurnRelation.getRestriction() == OSMTurnRelation.Type.ONLY && wayId != osmTurnRelation.getOsmIdTo()
+                        || osmTurnRelation.getRestriction() == OSMTurnRelation.Type.NOT && wayId == osmTurnRelation.getOsmIdTo() && wayId >= 0) {
+                    final TurnCostTableEntry entry = new TurnCostTableEntry();
+                    entry.nodeVia = nodeVia;
+                    entry.edgeFrom = edgeIdFrom;
+                    entry.edgeTo = iter.getEdge();
+                    entry.flags = encoder.getTurnFlags(true, 0);
+                    entries.add(entry);
+
+                    if (osmTurnRelation.getRestriction() == OSMTurnRelation.Type.NOT)
+                        break;
+                }
+            }
+            return entries;
+        } catch (Exception e) {
+            throw new IllegalStateException("Could not built turn table entry for relation of node with osmId:" + osmTurnRelation.getViaOsmNodeId(), e);
+        }
+    }
+
+    /**
+     * Helper class to processing purposes only
+     */
+    public static class TurnCostTableEntry {
+        public int edgeFrom;
+        public int nodeVia;
+        public int edgeTo;
+        public long flags;
+
+        /**
+         * @return an unique id (edgeFrom, edgeTo) to avoid duplicate entries if multiple encoders
+         * are involved.
+         */
+        public long getItemId() {
+            return ((long) edgeFrom) << 32 | ((long) edgeTo);
+        }
+
+        @Override
+        public String toString() {
+            return "*-(" + edgeFrom + ")->" + nodeVia + "-(" + edgeTo + ")->*";
+        }
+    }
+
     /**
      * Filter method, override in subclass
      */
diff --git a/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMTurnRelation.java b/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMTurnRelation.java
deleted file mode 100644
index 966702bf8..000000000
--- a/reader-osm/src/main/java/com/graphhopper/reader/osm/OSMTurnRelation.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- *  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.osm;
-
-import com.graphhopper.routing.util.TurnCostEncoder;
-import com.graphhopper.util.EdgeExplorer;
-import com.graphhopper.util.EdgeIterator;
-
-import java.util.*;
-
-/**
- * Helper object which gives node cost entries for a given OSM-relation of type "restriction"
- * <p>
- *
- * @author Karl Hübner
- */
-public class OSMTurnRelation {
-    private final long fromOsmWayId;
-    private final long viaOsmNodeId;
-    private final long toOsmWayId;
-    private final Type restriction;
-
-    OSMTurnRelation(long fromWayID, long viaNodeID, long toWayID, Type restrictionType) {
-        this.fromOsmWayId = fromWayID;
-        this.viaOsmNodeId = viaNodeID;
-        this.toOsmWayId = toWayID;
-        this.restriction = restrictionType;
-    }
-
-    long getOsmIdFrom() {
-        return fromOsmWayId;
-    }
-
-    long getOsmIdTo() {
-        return toOsmWayId;
-    }
-
-    /**
-     * Transforms this relation into a collection of turn cost entries
-     * <p>
-     *
-     * @param edgeOutExplorer an edge filter which only allows outgoing edges
-     * @param edgeInExplorer  an edge filter which only allows incoming edges
-     * @return a collection of node cost entries which can be added to the graph later
-     */
-    public Collection<TurnCostTableEntry> getRestrictionAsEntries(TurnCostEncoder encoder,
-                                                                  EdgeExplorer edgeOutExplorer, EdgeExplorer edgeInExplorer, OSMReader osmReader) {
-        int nodeVia = osmReader.getInternalNodeIdOfOsmNode(this.viaOsmNodeId);
-
-        try {
-            // street with restriction was not included (access or tag limits etc)
-            if (nodeVia == OSMReader.EMPTY_NODE)
-                return Collections.emptyList();
-
-            int edgeIdFrom = EdgeIterator.NO_EDGE;
-
-            // get all incoming edges and receive the edge which is defined by fromOsm
-            EdgeIterator iter = edgeInExplorer.setBaseNode(nodeVia);
-
-            while (iter.next()) {
-                if (osmReader.getOsmIdOfInternalEdge(iter.getEdge()) == this.fromOsmWayId) {
-                    edgeIdFrom = iter.getEdge();
-                    break;
-                }
-            }
-
-            if (!EdgeIterator.Edge.isValid(edgeIdFrom))
-                return Collections.emptyList();
-
-            final Collection<TurnCostTableEntry> entries = new ArrayList<>();
-            // get all outgoing edges of the via node
-            iter = edgeOutExplorer.setBaseNode(nodeVia);
-            // for TYPE_ONLY_* we add ALL restrictions (from, via, * ) EXCEPT the given turn
-            // for TYPE_NOT_*  we add ONE restriction  (from, via, to)
-            while (iter.next()) {
-                int edgeId = iter.getEdge();
-                long wayId = osmReader.getOsmIdOfInternalEdge(edgeId);
-                if (edgeId != edgeIdFrom && this.restriction == Type.ONLY && wayId != this.toOsmWayId
-                        || this.restriction == Type.NOT && wayId == this.toOsmWayId && wayId >= 0) {
-                    final TurnCostTableEntry entry = new TurnCostTableEntry();
-                    entry.nodeVia = nodeVia;
-                    entry.edgeFrom = edgeIdFrom;
-                    entry.edgeTo = iter.getEdge();
-                    entry.flags = encoder.getTurnFlags(true, 0);
-                    entries.add(entry);
-
-                    if (this.restriction == Type.NOT)
-                        break;
-                }
-            }
-            return entries;
-        } catch (Exception e) {
-            throw new IllegalStateException("Could not built turn table entry for relation of node with osmId:" + this.viaOsmNodeId, e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "*-(" + fromOsmWayId + ")->" + viaOsmNodeId + "-(" + toOsmWayId + ")->*";
-    }
-
-    enum Type {
-        UNSUPPORTED, NOT, ONLY;
-
-        private static final Map<String, Type> tags = new HashMap<>();
-
-        static {
-            tags.put("no_left_turn", NOT);
-            tags.put("no_right_turn", NOT);
-            tags.put("no_straight_on", NOT);
-            tags.put("no_u_turn", NOT);
-            tags.put("only_right_turn", ONLY);
-            tags.put("only_left_turn", ONLY);
-            tags.put("only_straight_on", ONLY);
-        }
-
-        public static Type getRestrictionType(String tag) {
-            Type result = null;
-            if (tag != null) {
-                result = tags.get(tag);
-            }
-            return (result != null) ? result : UNSUPPORTED;
-        }
-    }
-
-    /**
-     * Helper class to processing purposes only
-     */
-    public static class TurnCostTableEntry {
-        public int edgeFrom;
-        public int nodeVia;
-        public int edgeTo;
-        public long flags;
-
-        /**
-         * @return an unique id (edgeFrom, edgeTo) to avoid duplicate entries if multiple encoders
-         * are involved.
-         */
-        public long getItemId() {
-            return ((long) edgeFrom) << 32 | ((long) edgeTo);
-        }
-
-        @Override
-        public String toString() {
-            return "*-(" + edgeFrom + ")->" + nodeVia + "-(" + edgeTo + ")->*";
-        }
-    }
-
-}


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java b/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java
index 377ac98d5..04adb88b8 100644
--- a/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.reader.ReaderNode;
 import com.graphhopper.reader.ReaderWay;
 import com.graphhopper.routing.profiles.*;
@@ -651,4 +652,34 @@ public class CarFlagEncoderTest {
         assertEquals(2.5, encoder.getFerrySpeed(way), .1);
         assertEquals(.5, lowFactorCar.getFerrySpeed(way), .1);
     }
+
+    @Test
+    public void testAcceptsTurnRelation() {
+        List<String> vehicleTypesExcept = new ArrayList<>();
+        OSMTurnRelation osmTurnRelation = new OSMTurnRelation(1, 1, 1, OSMTurnRelation.Type.NOT);
+        assertTrue(encoder.acceptsTurnRelation(osmTurnRelation));
+
+        vehicleTypesExcept.add("bus");
+        osmTurnRelation.addVehicleTypesExcept(vehicleTypesExcept);
+        assertTrue(encoder.acceptsTurnRelation(osmTurnRelation));
+
+        vehicleTypesExcept.clear();
+        vehicleTypesExcept.add("vehicle");
+        osmTurnRelation.addVehicleTypesExcept(vehicleTypesExcept);
+        assertFalse(encoder.acceptsTurnRelation(osmTurnRelation));
+
+        vehicleTypesExcept.clear();
+        vehicleTypesExcept.add("motor_vehicle");
+        vehicleTypesExcept.add("vehicle");
+        osmTurnRelation.addVehicleTypesExcept(vehicleTypesExcept);
+        assertFalse(encoder.acceptsTurnRelation(osmTurnRelation));
+
+        vehicleTypesExcept.clear();
+        osmTurnRelation.setVehicleTypeRestricted("bus");
+        osmTurnRelation.addVehicleTypesExcept(vehicleTypesExcept);
+        assertFalse(encoder.acceptsTurnRelation(osmTurnRelation));
+
+        osmTurnRelation.setVehicleTypeRestricted("vehicle");
+        assertTrue(encoder.acceptsTurnRelation(osmTurnRelation));
+    }
 }
diff --git a/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java b/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java
index d12f3922f..f7281ab18 100644
--- a/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.util;
 
+import com.graphhopper.reader.OSMTurnRelation;
 import com.graphhopper.reader.ReaderRelation;
 import com.graphhopper.reader.ReaderWay;
 import com.graphhopper.routing.profiles.BooleanEncodedValue;
@@ -104,6 +105,14 @@ public class EncodingManagerTest {
                 return "my_properties";
             }
 
+            @Override
+            public boolean acceptsTurnRelation(OSMTurnRelation relation) {
+                if (relation.isVehicleTypeConcernedByTurnRestriction(restrictions)) {
+                    return true;
+                }
+                return false;
+            }
+
             @Override
             public long handleRelationTags(long oldRelationFlags, ReaderRelation relation) {
                 return 0;
diff --git a/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java b/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java
index 1a25f0f1c..9fdcd3858 100644
--- a/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java
@@ -22,10 +22,7 @@ import com.graphhopper.GHRequest;
 import com.graphhopper.GHResponse;
 import com.graphhopper.GraphHopper;
 import com.graphhopper.GraphHopperIT;
-import com.graphhopper.reader.DataReader;
-import com.graphhopper.reader.ReaderNode;
-import com.graphhopper.reader.ReaderRelation;
-import com.graphhopper.reader.ReaderWay;
+import com.graphhopper.reader.*;
 import com.graphhopper.reader.dem.ElevationProvider;
 import com.graphhopper.reader.dem.SRTMProvider;
 import com.graphhopper.routing.profiles.*;
@@ -69,6 +66,7 @@ public class OSMReaderTest {
     private final String fileBarriers = "test-barriers.xml";
     private final String fileTurnRestrictions = "test-restrictions.xml";
     private final String fileRoadAttributes = "test-road-attributes.xml";
+    private final String fileConditionalTurnRestrictions = "test-conditional-turn-restrictions.xml";
     private final String dir = "./target/tmp/test-db";
     private CarFlagEncoder carEncoder;
     private BooleanEncodedValue carAccessEnc;
@@ -725,9 +723,11 @@ public class OSMReaderTest {
      */
     @Test
     public void testTurnFlagCombination() {
-        final OSMTurnRelation.TurnCostTableEntry turnCostEntry_car = new OSMTurnRelation.TurnCostTableEntry();
-        final OSMTurnRelation.TurnCostTableEntry turnCostEntry_foot = new OSMTurnRelation.TurnCostTableEntry();
-        final OSMTurnRelation.TurnCostTableEntry turnCostEntry_bike = new OSMTurnRelation.TurnCostTableEntry();
+        final OSMReader.TurnCostTableEntry turnCostEntry_car = new OSMReader.TurnCostTableEntry();
+        final OSMReader.TurnCostTableEntry turnCostEntry_foot = new OSMReader.TurnCostTableEntry();
+        final OSMReader.TurnCostTableEntry turnCostEntry_bike = new OSMReader.TurnCostTableEntry();
+
+        final OSMTurnRelation osmTurnRelation = new OSMTurnRelation(1, 1,1, OSMTurnRelation.Type.NOT);
 
         CarFlagEncoder car = new CarFlagEncoder(5, 5, 24);
         FootFlagEncoder foot = new FootFlagEncoder();
@@ -737,7 +737,7 @@ public class OSMReaderTest {
         GraphHopperStorage ghStorage = new GraphBuilder(manager).create();
         OSMReader reader = new OSMReader(ghStorage) {
             @Override
-            public Collection<OSMTurnRelation.TurnCostTableEntry> analyzeTurnRelation(FlagEncoder encoder,
+            public Collection<OSMReader.TurnCostTableEntry> analyzeTurnRelation(FlagEncoder encoder,
                                                                                       OSMTurnRelation turnRelation) {
                 // simulate by returning one turn cost entry directly
                 if (encoder.toString().equalsIgnoreCase("car")) {
@@ -770,12 +770,12 @@ public class OSMReaderTest {
         long assertFlag2 = turnCostEntry_bike.flags;
 
         // combine flags of all encoders
-        Collection<OSMTurnRelation.TurnCostTableEntry> entries = reader.analyzeTurnRelation(null);
+        Collection<OSMReader.TurnCostTableEntry> entries = reader.analyzeTurnRelation(osmTurnRelation);
 
         // we expect two different turnCost entries
         assertEquals(2, entries.size());
 
-        for (OSMTurnRelation.TurnCostTableEntry entry : entries) {
+        for (OSMReader.TurnCostTableEntry entry : entries) {
             if (entry.edgeFrom == 1) {
                 // the first entry provides turn flags for car and foot only
                 assertEquals(assertFlag1, entry.flags);
@@ -800,6 +800,70 @@ public class OSMReaderTest {
         }
     }
 
+    @Test
+    public void testConditionalTurnRestriction() {
+        GraphHopper hopper = new GraphHopperFacade(fileConditionalTurnRestrictions, true, "").
+                importOrLoad();
+
+        Graph graph = hopper.getGraphHopperStorage();
+        assertEquals(8, graph.getNodes());
+        assertTrue(graph.getExtension() instanceof TurnCostExtension);
+        TurnCostExtension tcStorage = (TurnCostExtension) graph.getExtension();
+
+        int n1 = AbstractGraphStorageTester.getIdOf(graph, 50, 10);
+        int n2 = AbstractGraphStorageTester.getIdOf(graph, 52, 10);
+        int n3 = AbstractGraphStorageTester.getIdOf(graph, 52, 11);
+        int n4 = AbstractGraphStorageTester.getIdOf(graph, 52, 12);
+        int n5 = AbstractGraphStorageTester.getIdOf(graph, 50, 12);
+        int n6 = AbstractGraphStorageTester.getIdOf(graph, 51, 11);
+        int n8 = AbstractGraphStorageTester.getIdOf(graph, 54, 11);
+
+        int edge1_6 = GHUtility.getEdge(graph, n1, n6).getEdge();
+        int edge2_3 = GHUtility.getEdge(graph, n2, n3).getEdge();
+        int edge3_4 = GHUtility.getEdge(graph, n3, n4).getEdge();
+        int edge3_8 = GHUtility.getEdge(graph, n3, n8).getEdge();
+
+        int edge3_2 = GHUtility.getEdge(graph, n3, n2).getEdge();
+        int edge4_3 = GHUtility.getEdge(graph, n4, n3).getEdge();
+        int edge8_3 = GHUtility.getEdge(graph, n8, n3).getEdge();
+
+        int edge4_5 = GHUtility.getEdge(graph, n4, n5).getEdge();
+        int edge5_6 = GHUtility.getEdge(graph, n5, n6).getEdge();
+        int edge5_1 = GHUtility.getEdge(graph, n5, n1).getEdge();
+
+        // (2-3)->(3-4) only_straight_on except bicycle = (2-3)->(3-8) restricted for car
+        // (4-3)->(3-8) no_right_turn dedicated to motorcar = (4-3)->(3-8) restricted for car
+        assertTrue(carEncoder.getTurnCost(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_8)) > 0);
+        assertTrue(carEncoder.getTurnCost(tcStorage.getTurnCostFlags(edge4_3, n3, edge3_8)) > 0);
+        assertFalse(carEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_4)));
+        assertFalse(carEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_2)));
+        assertFalse(carEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_4)));
+        assertFalse(carEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge4_3, n3, edge3_2)));
+        assertFalse(carEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge8_3, n3, edge3_2)));
+
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_8)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge4_3, n3, edge3_8)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_4)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_2)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge2_3, n3, edge3_4)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge4_3, n3, edge3_2)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge8_3, n3, edge3_2)));
+
+        // u-turn except bus;bicycle restriction for (6-1)->(1-6) but not for (1-6)->(6-1)
+        assertTrue(carEncoder.getTurnCost(tcStorage.getTurnCostFlags(edge1_6, n1, edge1_6)) > 0);
+        assertFalse(carEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge1_6, n6, edge1_6)));
+
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge1_6, n1, edge1_6)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge1_6, n6, edge1_6)));
+
+        // (4-5)->(5-6) right_turn_only dedicated to motorcar = (4-5)->(5-1) restricted
+        assertFalse(carEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge4_5, n5, edge5_6)));
+        assertTrue(carEncoder.getTurnCost(tcStorage.getTurnCostFlags(edge4_5, n5, edge5_1)) > 0);
+
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge4_5, n5, edge5_6)));
+        assertFalse(bikeEncoder.isTurnRestricted(tcStorage.getTurnCostFlags(edge4_5, n5, edge5_1)));
+    }
+
     @Test
     public void testPreferredLanguage() {
         GraphHopper hopper = new GraphHopperFacade(file1, false, "de").importOrLoad();
diff --git a/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMTurnRelationTest.java b/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMTurnRelationTest.java
index 9faf348c7..65fcb0615 100644
--- a/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMTurnRelationTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/reader/osm/OSMTurnRelationTest.java
@@ -17,7 +17,8 @@
  */
 package com.graphhopper.reader.osm;
 
-import com.graphhopper.reader.osm.OSMTurnRelation.Type;
+import com.graphhopper.reader.OSMTurnRelation;
+import com.graphhopper.reader.OSMTurnRelation.Type;
 import com.graphhopper.routing.EdgeBasedRoutingAlgorithmTest;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
@@ -70,12 +71,12 @@ public class OSMTurnRelationTest {
 
         // TYPE == ONLY
         OSMTurnRelation instance = new OSMTurnRelation(4, 3, 3, Type.ONLY);
-        Collection<OSMTurnRelation.TurnCostTableEntry> result
-                = instance.getRestrictionAsEntries(encoder, edgeExplorer, edgeExplorer, osmReader);
+        Collection<OSMReader.TurnCostTableEntry> result
+                = OSMReader.getRestrictionAsEntries(instance, encoder, edgeExplorer, edgeExplorer, osmReader);
 
         assertEquals(2, result.size());
-        Iterator<OSMTurnRelation.TurnCostTableEntry> iter = result.iterator();
-        OSMTurnRelation.TurnCostTableEntry entry = iter.next();
+        Iterator<OSMReader.TurnCostTableEntry> iter = result.iterator();
+        OSMReader.TurnCostTableEntry entry = iter.next();
         assertEquals(4, entry.edgeFrom);
         assertEquals(6, entry.edgeTo);
         assertEquals(3, entry.nodeVia);
@@ -87,7 +88,7 @@ public class OSMTurnRelationTest {
 
         // TYPE == NOT
         instance = new OSMTurnRelation(4, 3, 3, Type.NOT);
-        result = instance.getRestrictionAsEntries(encoder, edgeExplorer, edgeExplorer, osmReader);
+        result = OSMReader.getRestrictionAsEntries(instance, encoder, edgeExplorer, edgeExplorer, osmReader);
 
         assertEquals(1, result.size());
         iter = result.iterator();
diff --git a/reader-osm/src/test/resources/com/graphhopper/reader/osm/test-conditional-turn-restrictions.xml b/reader-osm/src/test/resources/com/graphhopper/reader/osm/test-conditional-turn-restrictions.xml
new file mode 100644
index 000000000..36e739711
--- /dev/null
+++ b/reader-osm/src/test/resources/com/graphhopper/reader/osm/test-conditional-turn-restrictions.xml
@@ -0,0 +1,129 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="pbf2osm">
+    <!-- 
+       1~~~2
+       |\  |
+       | 6 3~~8
+       |/  |
+       5~~~4~~7
+       
+       
+      (2,3)->(3,4): straight_only
+      (4,3)->(3,8): no_right_turn
+      (4,5)->(5,6): right_turn_only
+      (6,1)->(1,6): no_u_turn
+    -->
+
+    <node id="1" lat="50" lon="10"> </node>
+    <node id="2" lat="52" lon="10"> </node>
+    <node id="3" lat="52" lon="11"> </node>
+    <node id="4" lat="52" lon="12"> </node>
+    <node id="5" lat="50" lon="12"> </node>
+    <node id="6" lat="51" lon="11"> </node>
+    <node id="7" lat="54" lon="12"> </node>
+    <node id="8" lat="54" lon="11"> </node>
+                      
+    <!-- 1-2 -->
+    <way id="12">
+        <nd ref="1"/>
+        <nd ref="2"/>
+        <tag k="highway" v="motorway" />
+    </way>    
+    
+    <!-- 2-3 -->
+    <way id="23">
+        <nd ref="2"/>        
+        <nd ref="3"/>
+        <tag k="highway" v="motorway" />
+    </way>
+    
+    <!-- 3-4 -->
+    <way id="34">
+        <nd ref="3"/>     
+        <nd ref="4"/>
+        <tag k="highway" v="motorway" />
+    </way>     
+    
+    <!-- 4-5 -->
+    <way id="45">
+        <nd ref="5"/>     
+        <nd ref="4"/>
+        <tag k="highway" v="motorway" />
+    </way>  
+    
+    <!-- 5-1 -->
+    <way id="51">
+        <nd ref="1"/>     
+        <nd ref="5"/>
+        <tag k="highway" v="motorway" />
+    </way>
+    
+    <!-- 5-6 -->
+    <way id="56">
+        <nd ref="6"/>     
+        <nd ref="5"/>
+        <tag k="highway" v="motorway" />
+    </way>
+    
+    <!-- 6-1 -->
+    <way id="61">
+        <nd ref="1"/>     
+        <nd ref="6"/>
+        <tag k="highway" v="motorway" />
+    </way>
+    
+    <!-- 3-8 -->
+    <way id="38">
+        <nd ref="3"/>     
+        <nd ref="8"/>
+        <tag k="highway" v="motorway" />
+    </way>
+    
+    <!-- 4-7 -->
+    <way id="47">
+        <nd ref="4"/>     
+        <nd ref="7"/>
+        <tag k="highway" v="motorway" />
+    </way>
+    
+    <way id="510">
+        <nd ref="5"/>     
+        <nd ref="10"/>
+        <tag k="highway" v="cycleway" />
+    </way>
+        
+    <relation id="1001">
+        <member type="way" ref="23" role="from"/>
+        <member type="way" ref="34" role="to"/>
+        <member type="node" ref="3" role="via"/>
+        <tag k="restriction" v="only_straight_on"/>
+        <tag k="type" v="restriction"/>
+		<tag k="except" v="bicycle"/>
+    </relation>
+        
+    <relation id="1002">
+        <member type="way" ref="34" role="from"/>
+        <member type="way" ref="38" role="to"/>
+        <member type="node" ref="3" role="via"/>
+        <tag k="restriction:motorcar" v="no_right_turn"/>
+        <tag k="type" v="restriction"/>
+    </relation>
+    
+    <relation id="1003">
+        <member type="way" ref="45" role="from"/>
+        <member type="way" ref="56" role="to"/>
+        <member type="node" ref="5" role="via"/>
+        <tag k="restriction:motorcar" v="only_right_turn"/>
+        <tag k="type" v="restriction"/>
+    </relation>
+    
+    <relation id="1004">
+        <member type="way" ref="61" role="from"/>
+        <member type="way" ref="61" role="to"/>
+        <member type="node" ref="1" role="via"/>
+        <tag k="restriction" v="no_u_turn"/>
+        <tag k="type" v="restriction"/>
+		<tag k="except" v="bus;bicycle"/>
+    </relation>
+    
+</osm>