001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools;
003
004import java.awt.geom.Area;
005import java.io.IOException;
006import java.io.InputStream;
007import java.util.ArrayList;
008import java.util.Collection;
009
010import org.openstreetmap.josm.data.coor.LatLon;
011import org.openstreetmap.josm.data.osm.BBox;
012import org.openstreetmap.josm.data.osm.DataSet;
013import org.openstreetmap.josm.data.osm.Way;
014import org.openstreetmap.josm.io.CachedFile;
015import org.openstreetmap.josm.io.IllegalDataException;
016import org.openstreetmap.josm.io.OsmReader;
017import org.openstreetmap.josm.tools.GeoPropertyIndex.GeoProperty;
018import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
019
020/**
021 * Look up, if there is right- or left-hand traffic at a certain place.
022 */
023public final class RightAndLefthandTraffic {
024
025    private static class RLTrafficGeoProperty implements GeoProperty<Boolean> {
026
027        @Override
028        public Boolean get(LatLon ll) {
029            for (Area a : leftHandTrafficPolygons) {
030                if (a.contains(ll.lon(), ll.lat()))
031                    return Boolean.TRUE;
032            }
033            return Boolean.FALSE;
034        }
035
036        @Override
037        public Boolean get(BBox box) {
038            Area abox = new Area(box.toRectangle());
039            for (Area a : leftHandTrafficPolygons) {
040                PolygonIntersection is = Geometry.polygonIntersection(abox, a, 1e-10 /* using deg and not meters */);
041                if (is == PolygonIntersection.FIRST_INSIDE_SECOND)
042                    return Boolean.TRUE;
043                if (is != PolygonIntersection.OUTSIDE)
044                    return null;
045            }
046            return Boolean.FALSE;
047        }
048    }
049
050    private static volatile Collection<Area> leftHandTrafficPolygons;
051    private static volatile GeoPropertyIndex<Boolean> rlCache;
052
053    private RightAndLefthandTraffic() {
054        // Hide implicit public constructor for utility classes
055    }
056
057    /**
058     * Check if there is right-hand traffic at a certain location.
059     *
060     * TODO: Synchronization can be refined inside the {@link GeoPropertyIndex}
061     *       as most look-ups are read-only.
062     * @param ll the coordinates of the point
063     * @return true if there is right-hand traffic, false if there is left-hand traffic
064     */
065    public static synchronized boolean isRightHandTraffic(LatLon ll) {
066        if (leftHandTrafficPolygons == null) {
067            initialize();
068        }
069        return !rlCache.get(ll);
070    }
071
072    private static void initialize() {
073        leftHandTrafficPolygons = new ArrayList<>();
074        try (InputStream is = new CachedFile("resource://data/left-right-hand-traffic.osm").getInputStream()) {
075            DataSet data = OsmReader.parseDataSet(is, null);
076            for (Way w : data.getWays()) {
077                leftHandTrafficPolygons.add(Geometry.getAreaLatLon(w.getNodes()));
078            }
079        } catch (IOException | IllegalDataException ex) {
080            throw new RuntimeException(ex);
081        }
082        rlCache = new GeoPropertyIndex<Boolean>(new RLTrafficGeoProperty(), 24);
083    }
084}