source: josm/trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java@ 13692

Last change on this file since 13692 was 13647, checked in by Don-vip, 6 years ago

see #16204 - Allow to start and close JOSM in WebStart sandbox mode (where every external access is denied). This was very useful to reproduce some very tricky bugs that occured in real life but were almost impossible to diagnose.

  • Property svn:eol-style set to native
File size: 7.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.io.File;
5import java.io.IOException;
6import java.io.InputStream;
7import java.io.OutputStreamWriter;
8import java.io.PrintWriter;
9import java.io.Writer;
10import java.nio.charset.StandardCharsets;
11import java.nio.file.Files;
12import java.nio.file.InvalidPathException;
13import java.nio.file.Paths;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.List;
18import java.util.Set;
19
20import org.openstreetmap.josm.actions.JoinAreasAction;
21import org.openstreetmap.josm.actions.JoinAreasAction.JoinAreasResult;
22import org.openstreetmap.josm.actions.JoinAreasAction.Multipolygon;
23import org.openstreetmap.josm.command.PurgeCommand;
24import org.openstreetmap.josm.data.coor.LatLon;
25import org.openstreetmap.josm.data.osm.DataSet;
26import org.openstreetmap.josm.data.osm.DownloadPolicy;
27import org.openstreetmap.josm.data.osm.OsmPrimitive;
28import org.openstreetmap.josm.data.osm.Relation;
29import org.openstreetmap.josm.data.osm.RelationMember;
30import org.openstreetmap.josm.data.osm.UploadPolicy;
31import org.openstreetmap.josm.data.osm.Way;
32import org.openstreetmap.josm.io.IllegalDataException;
33import org.openstreetmap.josm.io.OsmReader;
34import org.openstreetmap.josm.io.OsmWriter;
35import org.openstreetmap.josm.io.OsmWriterFactory;
36import org.openstreetmap.josm.spi.preferences.Config;
37
38/**
39 * Look up, if there is right- or left-hand traffic at a certain place.
40 */
41public final class RightAndLefthandTraffic {
42
43 private static final String DRIVING_SIDE = "driving_side";
44 private static final String LEFT = "left";
45 private static final String RIGHT = "right";
46
47 private static volatile GeoPropertyIndex<Boolean> rlCache;
48
49 private RightAndLefthandTraffic() {
50 // Hide implicit public constructor for utility classes
51 }
52
53 /**
54 * Check if there is right-hand traffic at a certain location.
55 *
56 * @param ll the coordinates of the point
57 * @return true if there is right-hand traffic, false if there is left-hand traffic
58 */
59 public static synchronized boolean isRightHandTraffic(LatLon ll) {
60 return !rlCache.get(ll);
61 }
62
63 /**
64 * Initializes Right and lefthand traffic data.
65 * TODO: Synchronization can be refined inside the {@link GeoPropertyIndex} as most look-ups are read-only.
66 */
67 public static synchronized void initialize() {
68 Collection<Way> optimizedWays = loadOptimizedBoundaries();
69 if (optimizedWays.isEmpty()) {
70 optimizedWays = computeOptimizedBoundaries();
71 try {
72 saveOptimizedBoundaries(optimizedWays);
73 } catch (IOException | SecurityException e) {
74 Logging.log(Logging.LEVEL_ERROR, "Unable to save optimized boundaries", e);
75 }
76 }
77 rlCache = new GeoPropertyIndex<>(new DefaultGeoProperty(optimizedWays), 24);
78 }
79
80 private static Collection<Way> computeOptimizedBoundaries() {
81 Collection<Way> ways = new ArrayList<>();
82 Collection<OsmPrimitive> toPurge = new ArrayList<>();
83 // Find all outer ways of left-driving countries. Many of them are adjacent (African and Asian states)
84 DataSet data = Territories.getDataSet();
85 Collection<Relation> allRelations = data.getRelations();
86 Collection<Way> allWays = data.getWays();
87 for (Way w : allWays) {
88 if (LEFT.equals(w.get(DRIVING_SIDE))) {
89 addWayIfNotInner(ways, w);
90 }
91 }
92 for (Relation r : allRelations) {
93 if (r.isMultipolygon() && LEFT.equals(r.get(DRIVING_SIDE))) {
94 for (RelationMember rm : r.getMembers()) {
95 if (rm.isWay() && "outer".equals(rm.getRole()) && !RIGHT.equals(rm.getMember().get(DRIVING_SIDE))) {
96 addWayIfNotInner(ways, (Way) rm.getMember());
97 }
98 }
99 }
100 }
101 toPurge.addAll(allRelations);
102 toPurge.addAll(allWays);
103 toPurge.removeAll(ways);
104 // Remove ways from parent relations for following optimizations
105 for (Relation r : OsmPrimitive.getParentRelations(ways)) {
106 r.setMembers(null);
107 }
108 // Remove all tags to avoid any conflict
109 for (Way w : ways) {
110 w.removeAll();
111 }
112 // Purge all other ways and relations so dataset only contains lefthand traffic data
113 PurgeCommand.build(toPurge, null).executeCommand();
114 // Combine adjacent countries into a single polygon
115 Collection<Way> optimizedWays = new ArrayList<>();
116 List<Multipolygon> areas = JoinAreasAction.collectMultipolygons(ways);
117 if (areas != null) {
118 try {
119 JoinAreasResult result = new JoinAreasAction(false).joinAreas(areas);
120 if (result.hasChanges()) {
121 for (Multipolygon mp : result.getPolygons()) {
122 optimizedWays.add(mp.getOuterWay());
123 }
124 }
125 } catch (UserCancelException ex) {
126 Logging.warn(ex);
127 } catch (JosmRuntimeException ex) {
128 // Workaround to #10511 / #14185. To remove when #10511 is solved
129 Logging.error(ex);
130 }
131 }
132 if (optimizedWays.isEmpty()) {
133 // Problem: don't optimize
134 Logging.warn("Unable to join left-driving countries polygons");
135 optimizedWays.addAll(ways);
136 }
137 return optimizedWays;
138 }
139
140 /**
141 * Adds w to ways, except if it is an inner way of another lefthand driving multipolygon,
142 * as Lesotho in South Africa and Cyprus village in British Cyprus base.
143 * @param ways ways
144 * @param w way
145 */
146 private static void addWayIfNotInner(Collection<Way> ways, Way w) {
147 Set<Way> s = Collections.singleton(w);
148 for (Relation r : OsmPrimitive.getParentRelations(s)) {
149 if (r.isMultipolygon() && LEFT.equals(r.get(DRIVING_SIDE)) &&
150 "inner".equals(r.getMembersFor(s).iterator().next().getRole())) {
151 if (Logging.isDebugEnabled()) {
152 Logging.debug("Skipping {0} because inner part of {1}", w.get("name:en"), r.get("name:en"));
153 }
154 return;
155 }
156 }
157 ways.add(w);
158 }
159
160 private static void saveOptimizedBoundaries(Collection<Way> optimizedWays) throws IOException {
161 DataSet ds = optimizedWays.iterator().next().getDataSet();
162 File file = new File(Config.getDirs().getCacheDirectory(true), "left-right-hand-traffic.osm");
163 try (Writer writer = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8);
164 OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, ds.getVersion())
165 ) {
166 w.header(DownloadPolicy.NORMAL, UploadPolicy.DISCOURAGED);
167 w.writeContent(ds);
168 w.footer();
169 }
170 }
171
172 private static Collection<Way> loadOptimizedBoundaries() {
173 try (InputStream is = Files.newInputStream(Paths.get(
174 Config.getDirs().getCacheDirectory(false).getPath(), "left-right-hand-traffic.osm"))) {
175 return OsmReader.parseDataSet(is, null).getWays();
176 } catch (IllegalDataException | IOException | InvalidPathException | SecurityException ex) {
177 Logging.trace(ex);
178 return Collections.emptyList();
179 }
180 }
181}
Note: See TracBrowser for help on using the repository browser.