Ignore:
Timestamp:
2016-11-13T00:29:31+01:00 (7 years ago)
Author:
Don-vip
Message:

see #9400 - see #10387 - see #12914 - initial implementation of boundaries file, replacing left-right-hand-traffic.osm, discourage contributors to use operator=RFF and operator=ERDF in France (territories rules must be manually eabled on existing installations)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java

    r9639 r11247  
    33
    44import java.awt.geom.Area;
     5import java.io.File;
     6import java.io.FileInputStream;
     7import java.io.FileOutputStream;
    58import java.io.IOException;
    69import java.io.InputStream;
     10import java.io.OutputStreamWriter;
     11import java.io.PrintWriter;
     12import java.io.Writer;
     13import java.nio.charset.StandardCharsets;
    714import java.util.ArrayList;
    815import java.util.Collection;
    9 
     16import java.util.Collections;
     17import java.util.List;
     18import java.util.Set;
     19
     20import org.openstreetmap.josm.Main;
     21import org.openstreetmap.josm.actions.JoinAreasAction;
     22import org.openstreetmap.josm.actions.JoinAreasAction.JoinAreasResult;
     23import org.openstreetmap.josm.actions.JoinAreasAction.Multipolygon;
     24import org.openstreetmap.josm.actions.PurgeAction;
    1025import org.openstreetmap.josm.data.coor.LatLon;
    1126import org.openstreetmap.josm.data.osm.BBox;
    1227import org.openstreetmap.josm.data.osm.DataSet;
     28import org.openstreetmap.josm.data.osm.OsmPrimitive;
     29import org.openstreetmap.josm.data.osm.Relation;
     30import org.openstreetmap.josm.data.osm.RelationMember;
    1331import org.openstreetmap.josm.data.osm.Way;
    14 import org.openstreetmap.josm.io.CachedFile;
    1532import org.openstreetmap.josm.io.IllegalDataException;
    1633import org.openstreetmap.josm.io.OsmReader;
     34import org.openstreetmap.josm.io.OsmWriter;
     35import org.openstreetmap.josm.io.OsmWriterFactory;
    1736import org.openstreetmap.josm.tools.GeoPropertyIndex.GeoProperty;
    1837import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
     
    5877     * Check if there is right-hand traffic at a certain location.
    5978     *
    60      * TODO: Synchronization can be refined inside the {@link GeoPropertyIndex}
    61      *       as most look-ups are read-only.
    6279     * @param ll the coordinates of the point
    6380     * @return true if there is right-hand traffic, false if there is left-hand traffic
    6481     */
    6582    public static synchronized boolean isRightHandTraffic(LatLon ll) {
    66         if (leftHandTrafficPolygons == null) {
    67             initialize();
    68         }
    6983        return !rlCache.get(ll);
    7084    }
    7185
    72     private static void initialize() {
     86    /**
     87     * Initializes Right and lefthand traffic data.
     88     * TODO: Synchronization can be refined inside the {@link GeoPropertyIndex} as most look-ups are read-only.
     89     */
     90    public static synchronized void initialize() {
    7391        leftHandTrafficPolygons = new ArrayList<>();
    74         try (CachedFile cf = new CachedFile("resource://data/left-right-hand-traffic.osm");
    75                 InputStream is = cf.getInputStream()) {
    76             DataSet data = OsmReader.parseDataSet(is, null);
    77             for (Way w : data.getWays()) {
    78                 leftHandTrafficPolygons.add(Geometry.getAreaLatLon(w.getNodes()));
    79             }
    80         } catch (IOException | IllegalDataException ex) {
     92        Collection<Way> optimizedWays = loadOptimizedBoundaries();
     93        if (optimizedWays.isEmpty()) {
     94            optimizedWays = computeOptimizedBoundaries();
     95            saveOptimizedBoundaries(optimizedWays);
     96        }
     97        for (Way w : optimizedWays) {
     98            leftHandTrafficPolygons.add(Geometry.getAreaLatLon(w.getNodes()));
     99        }
     100        rlCache = new GeoPropertyIndex<>(new RLTrafficGeoProperty(), 24);
     101    }
     102
     103    private static Collection<Way> computeOptimizedBoundaries() {
     104        Collection<Way> ways = new ArrayList<>();
     105        Collection<OsmPrimitive> toPurge = new ArrayList<>();
     106        // Find all outer ways of left-driving countries. Many of them are adjacent (African and Asian states)
     107        DataSet data = Territories.getDataSet();
     108        Collection<Relation> allRelations = data.getRelations();
     109        Collection<Way> allWays = data.getWays();
     110        for (Way w : allWays) {
     111            if ("left".equals(w.get("driving_side"))) {
     112                addWayIfNotInner(ways, w);
     113            }
     114        }
     115        for (Relation r : allRelations) {
     116            if (r.isMultipolygon() && "left".equals(r.get("driving_side"))) {
     117                for (RelationMember rm : r.getMembers()) {
     118                    if (rm.isWay() && "outer".equals(rm.getRole())) {
     119                        addWayIfNotInner(ways, (Way) rm.getMember());
     120                    }
     121                }
     122            }
     123        }
     124        toPurge.addAll(allRelations);
     125        toPurge.addAll(allWays);
     126        toPurge.removeAll(ways);
     127        // Remove ways from parent relations for following optimizations
     128        for (Relation r : OsmPrimitive.getParentRelations(ways)) {
     129            r.setMembers(null);
     130        }
     131        // Remove all tags to avoid any conflict
     132        for (Way w : ways) {
     133            w.removeAll();
     134        }
     135        // Purge all other ways and relations so dataset only contains lefthand traffic data
     136        new PurgeAction().doPurge(toPurge, false);
     137        // Combine adjacent countries into a single polygon
     138        Collection<Way> optimizedWays = new ArrayList<>();
     139        List<Multipolygon> areas = JoinAreasAction.collectMultipolygons(ways);
     140        if (areas != null) {
     141            try {
     142                JoinAreasResult result = new JoinAreasAction().joinAreas(areas);
     143                if (result.hasChanges) {
     144                    for (Multipolygon mp : result.polygons) {
     145                        optimizedWays.add(mp.outerWay);
     146                    }
     147                }
     148            } catch (UserCancelException ex) {
     149                Main.warn(ex);
     150            }
     151        }
     152        if (optimizedWays.isEmpty()) {
     153            // Problem: don't optimize
     154            Main.warn("Unable to join left-driving countries polygons");
     155            optimizedWays.addAll(ways);
     156        }
     157        return optimizedWays;
     158    }
     159
     160    /**
     161     * Adds w to ways, except if it is an inner way of another lefthand driving multipolygon,
     162     * as Lesotho in South Africa and Cyprus village in British Cyprus base.
     163     * @param ways ways
     164     * @param w way
     165     */
     166    private static void addWayIfNotInner(Collection<Way> ways, Way w) {
     167        Set<Way> s = Collections.singleton(w);
     168        for (Relation r : OsmPrimitive.getParentRelations(s)) {
     169            if (r.isMultipolygon() && "left".equals(r.get("driving_side")) &&
     170                "inner".equals(r.getMembersFor(s).iterator().next().getRole())) {
     171                if (Main.isDebugEnabled()) {
     172                    Main.debug("Skipping " + w.get("name:en") + " because inner part of " + r.get("name:en"));
     173                }
     174                return;
     175            }
     176        }
     177        ways.add(w);
     178    }
     179
     180    private static void saveOptimizedBoundaries(Collection<Way> optimizedWays) {
     181        DataSet ds = optimizedWays.iterator().next().getDataSet();
     182        File file = new File(Main.pref.getCacheDirectory(), "left-right-hand-traffic.osm");
     183        try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
     184             OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, ds.getVersion())
     185            ) {
     186            w.header(false);
     187            w.writeContent(ds);
     188            w.footer();
     189        } catch (IOException ex) {
    81190            throw new RuntimeException(ex);
    82191        }
    83         rlCache = new GeoPropertyIndex<>(new RLTrafficGeoProperty(), 24);
     192    }
     193
     194    private static Collection<Way> loadOptimizedBoundaries() {
     195        try (InputStream is = new FileInputStream(new File(Main.pref.getCacheDirectory(), "left-right-hand-traffic.osm"))) {
     196           return OsmReader.parseDataSet(is, null).getWays();
     197        } catch (IllegalDataException | IOException ex) {
     198            Main.trace(ex);
     199            return Collections.emptyList();
     200        }
    84201    }
    85202}
Note: See TracChangeset for help on using the changeset viewer.