Index: trunk/src/org/openstreetmap/josm/data/osm/BBox.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/BBox.java	(revision 7192)
+++ trunk/src/org/openstreetmap/josm/data/osm/BBox.java	(revision 7193)
@@ -2,4 +2,5 @@
 package org.openstreetmap.josm.data.osm;
 
+import java.awt.geom.Rectangle2D;
 import java.util.Arrays;
 
@@ -259,4 +260,8 @@
         return idx1;
     }
+    
+    public Rectangle2D toRectangle() {
+        return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin);
+    }
 
     @Override
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 7192)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 7193)
@@ -403,4 +403,6 @@
             case "unconnected":
                 return e.osm instanceof Node && OsmPrimitive.getFilteredList(e.osm.getReferrers(), Way.class).isEmpty();
+            case "righthandtraffic":
+                return ExpressionFactory.Functions.is_right_hand_traffic(e);
             }
             return false;
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(revision 7192)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(revision 7193)
@@ -26,4 +26,5 @@
 import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
 import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
+import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.gui.mappaint.Cascade;
@@ -32,4 +33,5 @@
 import org.openstreetmap.josm.tools.ColorHelper;
 import org.openstreetmap.josm.tools.Predicates;
+import org.openstreetmap.josm.tools.RightAndLefthandTraffic;
 import org.openstreetmap.josm.tools.Utils;
 
@@ -621,4 +623,10 @@
             return cs.getValue();
         }
+        
+        public static boolean is_right_hand_traffic(Environment env) {
+            if (env.osm instanceof Node)
+                return RightAndLefthandTraffic.isRightHandTraffic(((Node) env.osm).getCoor());
+            return RightAndLefthandTraffic.isRightHandTraffic(env.osm.getBBox().getCenter());
+        }
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/GeoPropertyIndex.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/GeoPropertyIndex.java	(revision 7193)
+++ trunk/src/org/openstreetmap/josm/tools/GeoPropertyIndex.java	(revision 7193)
@@ -0,0 +1,158 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.BBox;
+
+/**
+ * Fast index to look up properties of the earth surface.
+ * 
+ * It is expected that there is a relatively slow method to look up the property
+ * for a certain coordinate and that there are larger areas with a uniform
+ * property.
+ * 
+ * This index tries to find rectangles with uniform property and caches them.
+ * Rectangles are subdivided, if there are different properties within.
+ * (Up to a maximum level, when the slow method is used again.)
+ * 
+ * @param <T> the property (like land/water or nation)
+ */
+public class GeoPropertyIndex<T> {
+    
+    /**
+     * A method to look up a property of the earth surface.
+     * (User input for the index.)
+     * @param <T> the property
+     */
+    public interface GeoProperty<T> {
+        /**
+         * Look up the property for a point.
+         * @param ll the point coordinates
+         * @return property value at that point. Must not be null.
+         */
+        T get(LatLon ll);
+        /**
+         * Look up the property for a coordinate rectangle.
+         * @param box the rectangle
+         * @return the property, if it is the same in the entire rectangle;
+         * null otherwise
+         */
+        T get(BBox box);
+    }
+
+    private final int maxLevel;
+    private final GeoProperty<T> geoProp;
+    private final GPLevel<T> root;
+    private GPLevel<T> lastLevelUsed;
+    
+    private static final boolean DEBUG = false;
+
+    /**
+     * Create new GeoPropertyIndex.
+     * @param geoProp the input property that should be made faster by this index
+     * @param maxLevel 
+     */
+    public GeoPropertyIndex(GeoProperty<T> geoProp, int maxLevel) {
+        this.geoProp = geoProp;
+        this.maxLevel = maxLevel;
+        this.root = new GPLevel<T>(0, new BBox(-180, -90, 180, 90), null, this);
+        this.lastLevelUsed = root;
+    }
+    
+    /**
+     * Look up the property for a certain point.
+     * This gives the same result as {@link #geoProp#get(LatLon)}, but
+     * should be faster.
+     * @param ll the point coordinates
+     * @return property value at that point
+     */
+    public T get(LatLon ll) {
+        return lastLevelUsed.get(ll);
+    }
+    
+    public static int index(LatLon ll, int level) {
+        long noParts = 1 << level;
+        long x = ((long)((ll.lon() + 180.0) * noParts / 360.0)) & 1;
+        long y = ((long)((ll.lat() + 90.0) * noParts / 180.0)) & 1;
+        return (int) (2 * x + y);
+    }
+
+    protected static class GPLevel<T> {
+        private final T val;
+        private final int level;
+        private final BBox bbox;
+        private final GPLevel<T> parent;
+        private final GeoPropertyIndex<T> owner;
+
+        // child order by index is sw, nw, se, ne
+        private GPLevel<T>[] children;
+
+        public GPLevel(int level, BBox bbox, GPLevel<T> parent, GeoPropertyIndex<T> owner) {
+            this.level = level;
+            this.bbox = bbox;
+            this.parent = parent;
+            this.owner = owner;
+            this.val = owner.geoProp.get(bbox);
+        }
+
+        public T get(LatLon ll) {
+            if (bbox.bounds(ll))
+                return getBounded(ll);
+            if (DEBUG) System.err.print("up["+level+"]");
+            return parent.get(ll);
+        }
+        
+        private T getBounded(LatLon ll) {
+            if (DEBUG) System.err.print("GPLevel["+level+"]"+bbox+" ");
+            if (!bbox.bounds(ll)) {
+                throw new AssertionError("Point "+ll+" should be inside "+bbox);
+            }
+            if (val != null) {
+                if (DEBUG) System.err.println(" hit! "+val);
+                owner.lastLevelUsed = this;
+                return val;
+            }
+            if (level >= owner.maxLevel) {
+                if (DEBUG) System.err.println(" max level reached !");
+                return owner.geoProp.get(ll);
+            }
+            
+            if (children == null) {
+                @SuppressWarnings("unchecked")
+                GPLevel<T>[] tmp = (GPLevel<T>[]) new GPLevel[4];
+                this.children = tmp;
+            }
+            
+            int idx = index(ll, level+1);
+            if (children[idx] == null) {
+            double lon1, lat1;
+                switch (idx) {
+                    case 0: 
+                        lon1 = bbox.getTopLeftLon();
+                        lat1 = bbox.getBottomRightLat();
+                        break;
+                    case 1:
+                        lon1 = bbox.getTopLeftLon();
+                        lat1 = bbox.getTopLeftLat();
+                        break;
+                    case 2: 
+                        lon1 = bbox.getBottomRightLon();
+                        lat1 = bbox.getBottomRightLat();
+                        break;
+                    case 3: 
+                        lon1 = bbox.getBottomRightLon();
+                        lat1 = bbox.getTopLeftLat();
+                        break;
+                    default:
+                        throw new AssertionError();
+                }
+                if (DEBUG) System.err.println(" - new with idx "+idx);
+                LatLon center = bbox.getCenter();
+                BBox b = new BBox(lon1,lat1, center.lon(), center.lat());
+                children[idx] = new GPLevel<>(level + 1, b, this, owner);
+            }
+            return children[idx].getBounded(ll);
+        }
+
+    }
+}
Index: trunk/src/org/openstreetmap/josm/tools/Geometry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 7192)
+++ trunk/src/org/openstreetmap/josm/tools/Geometry.java	(revision 7193)
@@ -444,5 +444,5 @@
     /**
      * Returns the Area of a polygon, from its list of nodes.
-     * @param polygon List of nodes forming polygon
+     * @param polygon List of nodes forming polygon (EastNorth coordinates)
      * @return Area for the given list of nodes
      * @since 6841
@@ -469,4 +469,29 @@
         return new Area(path);
     }
+    
+    /**
+     * Returns the Area of a polygon, from its list of nodes.
+     * @param polygon List of nodes forming polygon (LatLon coordinates)
+     * @return Area for the given list of nodes
+     * @since 6841
+     */
+    public static Area getAreaLatLon(List<Node> polygon) {
+        Path2D path = new Path2D.Double();
+
+        boolean begin = true;
+        for (Node n : polygon) {
+            if (begin) {
+                path.moveTo(n.getCoor().lon(), n.getCoor().lat());
+                begin = false;
+            } else {
+                path.lineTo(n.getCoor().lon(), n.getCoor().lat());
+            }
+        }
+        if (!begin) {
+            path.closePath();
+        }
+
+        return new Area(path);
+    }
 
     /**
@@ -490,4 +515,15 @@
      */
     public static PolygonIntersection polygonIntersection(Area a1, Area a2) {
+        return polygonIntersection(a1, a2, 1.0);
+    }
+
+    /**
+     * Tests if two polygons intersect.
+     * @param a1 Area of first polygon
+     * @param a2 Area of second polygon
+     * @param eps an area threshold, everything below is considered an empty intersection
+     * @return intersection kind
+     */
+    public static PolygonIntersection polygonIntersection(Area a1, Area a2, double eps) {
 
         Area inter = new Area(a1);
@@ -496,5 +532,5 @@
         Rectangle bounds = inter.getBounds();
 
-        if (inter.isEmpty() || bounds.getHeight()*bounds.getWidth() <= 1.0) {
+        if (inter.isEmpty() || bounds.getHeight()*bounds.getWidth() <= eps) {
             return PolygonIntersection.OUTSIDE;
         } else if (inter.equals(a1)) {
Index: trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java	(revision 7193)
+++ trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java	(revision 7193)
@@ -0,0 +1,80 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.awt.geom.Area;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.BBox;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.io.MirroredInputStream;
+import org.openstreetmap.josm.io.OsmReader;
+import org.openstreetmap.josm.tools.GeoPropertyIndex.GeoProperty;
+import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
+
+/**
+ * Look up, if there is right- or left-hand traffic at a certain place.
+ */
+public class RightAndLefthandTraffic {
+    
+    private static class RLTrafficGeoProperty implements GeoProperty<Boolean> {
+
+        @Override
+        public Boolean get(LatLon ll) {
+            for (Area a : leftHandTrafficPolygons) {
+                if (a.contains(ll.lon(), ll.lat()))
+                    return true;
+            }
+            return false;
+        }
+
+        @Override
+        public Boolean get(BBox box) {
+            Area abox = new Area(box.toRectangle());
+            for (Area a : leftHandTrafficPolygons) {
+                PolygonIntersection is = Geometry.polygonIntersection(abox, a, 1e-10 /* using deg and not meters */);
+                if (is == PolygonIntersection.FIRST_INSIDE_SECOND)
+                    return true;
+                if (is != PolygonIntersection.OUTSIDE)
+                    return null;
+            }
+            return false;
+        }
+    }
+    
+    private static Collection<Area> leftHandTrafficPolygons;
+    private static GeoPropertyIndex<Boolean> rlCache;
+
+    /**
+     * Check if there is right-hand traffic at a certain location.
+     * 
+     * TODO: Synchronization can be refined inside the {@link GeoPropertyIndex}
+     *       as most look-ups are read-only. 
+     * @param ll the coordinates of the point
+     * @return true if there is right-hand traffic, false if there is left-hand traffic
+     */
+    public static synchronized boolean isRightHandTraffic(LatLon ll) {
+        if (leftHandTrafficPolygons == null) {
+            initialize();
+        }
+        return !rlCache.get(ll);
+    }
+
+    private static void initialize() {
+        leftHandTrafficPolygons = new ArrayList<>();
+        try (InputStream is = new MirroredInputStream("resource://data/left-right-hand-traffic.osm")) {
+            DataSet data = OsmReader.parseDataSet(is, null);
+            for (Way w : data.getWays()) {
+                leftHandTrafficPolygons.add(Geometry.getAreaLatLon(w.getNodes()));
+            }
+        } catch (IOException | IllegalDataException ex) {
+            throw new RuntimeException(ex);
+        }
+        rlCache = new GeoPropertyIndex<Boolean>(new RLTrafficGeoProperty(), 24);
+    }
+    
+}
