source: josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java@ 9171

Last change on this file since 9171 was 8919, checked in by Don-vip, 9 years ago

move UserCancelException class in tools package and use it where applicable

  • Property svn:eol-style set to native
File size: 9.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.corrector;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashMap;
9import java.util.List;
10import java.util.Locale;
11import java.util.Map;
12import java.util.regex.Matcher;
13import java.util.regex.Pattern;
14
15import org.openstreetmap.josm.command.Command;
16import org.openstreetmap.josm.data.osm.OsmPrimitive;
17import org.openstreetmap.josm.data.osm.OsmUtils;
18import org.openstreetmap.josm.data.osm.Relation;
19import org.openstreetmap.josm.data.osm.RelationMember;
20import org.openstreetmap.josm.data.osm.Tag;
21import org.openstreetmap.josm.data.osm.TagCollection;
22import org.openstreetmap.josm.data.osm.Way;
23import org.openstreetmap.josm.tools.UserCancelException;
24
25/**
26 * A ReverseWayTagCorrector handles necessary corrections of tags
27 * when a way is reversed. E.g. oneway=yes needs to be changed
28 * to oneway=-1 and vice versa.
29 *
30 * The Corrector offers the automatic resolution in an dialog
31 * for the user to confirm.
32 */
33public class ReverseWayTagCorrector extends TagCorrector<Way> {
34
35 private static final String SEPARATOR = "[:_]";
36
37 private static Pattern getPatternFor(String s) {
38 return getPatternFor(s, false);
39 }
40
41 private static Pattern getPatternFor(String s, boolean exactMatch) {
42 if (exactMatch) {
43 return Pattern.compile("(^)(" + s + ")($)");
44 } else {
45 return Pattern.compile("(^|.*" + SEPARATOR + ")(" + s + ")(" + SEPARATOR + ".*|$)",
46 Pattern.CASE_INSENSITIVE);
47 }
48 }
49
50 private static final Collection<Pattern> ignoredKeys = new ArrayList<>();
51 static {
52 for (String s : OsmPrimitive.getUninterestingKeys()) {
53 ignoredKeys.add(getPatternFor(s));
54 }
55 for (String s : new String[]{"name", "ref", "tiger:county"}) {
56 ignoredKeys.add(getPatternFor(s, false));
57 }
58 for (String s : new String[]{"tiger:county", "turn:lanes", "change:lanes", "placement"}) {
59 ignoredKeys.add(getPatternFor(s, true));
60 }
61 }
62
63 private static class StringSwitcher {
64
65 private final String a;
66 private final String b;
67 private final Pattern pattern;
68
69 StringSwitcher(String a, String b) {
70 this.a = a;
71 this.b = b;
72 this.pattern = getPatternFor(a + '|' + b);
73 }
74
75 public String apply(String text) {
76 Matcher m = pattern.matcher(text);
77
78 if (m.lookingAt()) {
79 String leftRight = m.group(2).toLowerCase(Locale.ENGLISH);
80
81 StringBuilder result = new StringBuilder();
82 result.append(text.substring(0, m.start(2)))
83 .append(leftRight.equals(a) ? b : a)
84 .append(text.substring(m.end(2)));
85
86 return result.toString();
87 }
88 return text;
89 }
90 }
91
92 /**
93 * Reverses a given tag.
94 * @since 5787
95 */
96 public static final class TagSwitcher {
97
98 private TagSwitcher() {
99 // Hide implicit public constructor for utility class
100 }
101
102 /**
103 * Reverses a given tag.
104 * @param tag The tag to reverse
105 * @return The reversed tag (is equal to <code>tag</code> if no change is needed)
106 */
107 public static Tag apply(final Tag tag) {
108 return apply(tag.getKey(), tag.getValue());
109 }
110
111 /**
112 * Reverses a given tag (key=value).
113 * @param key The tag key
114 * @param value The tag value
115 * @return The reversed tag (is equal to <code>key=value</code> if no change is needed)
116 */
117 public static Tag apply(final String key, final String value) {
118 String newKey = key;
119 String newValue = value;
120
121 if (key.startsWith("oneway") || key.endsWith("oneway")) {
122 if (OsmUtils.isReversed(value)) {
123 newValue = OsmUtils.trueval;
124 } else if (OsmUtils.isTrue(value)) {
125 newValue = OsmUtils.reverseval;
126 }
127 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
128 newKey = prefixSuffixSwitcher.apply(key);
129 if (!key.equals(newKey)) {
130 break;
131 }
132 }
133 } else if (key.startsWith("incline") || key.endsWith("incline")
134 || key.startsWith("direction") || key.endsWith("direction")) {
135 newValue = UP_DOWN.apply(value);
136 if (newValue.equals(value)) {
137 newValue = invertNumber(value);
138 }
139 } else if (key.endsWith(":forward") || key.endsWith(":backward")) {
140 // Change key but not left/right value (fix #8518)
141 newKey = FORWARD_BACKWARD.apply(key);
142 } else if (!ignoreKeyForCorrection(key)) {
143 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
144 newKey = prefixSuffixSwitcher.apply(key);
145 if (!key.equals(newKey)) {
146 break;
147 }
148 newValue = prefixSuffixSwitcher.apply(value);
149 if (!value.equals(newValue)) {
150 break;
151 }
152 }
153 }
154 return new Tag(newKey, newValue);
155 }
156 }
157
158 private static final StringSwitcher FORWARD_BACKWARD = new StringSwitcher("forward", "backward");
159 private static final StringSwitcher UP_DOWN = new StringSwitcher("up", "down");
160
161 private static final StringSwitcher[] stringSwitchers = new StringSwitcher[] {
162 new StringSwitcher("left", "right"),
163 new StringSwitcher("forwards", "backwards"),
164 new StringSwitcher("east", "west"),
165 new StringSwitcher("north", "south"),
166 FORWARD_BACKWARD, UP_DOWN
167 };
168
169 /**
170 * Tests whether way can be reversed without semantic change, i.e., whether tags have to be changed.
171 * Looks for keys like oneway, oneway:bicycle, cycleway:right:oneway, left/right.
172 * @param way way to test
173 * @return false if tags should be changed to keep semantic, true otherwise.
174 */
175 public static boolean isReversible(Way way) {
176 for (Tag tag : TagCollection.from(way)) {
177 if (!tag.equals(TagSwitcher.apply(tag))) {
178 return false;
179 }
180 }
181 return true;
182 }
183
184 public static List<Way> irreversibleWays(List<Way> ways) {
185 List<Way> newWays = new ArrayList<>(ways);
186 for (Way way : ways) {
187 if (isReversible(way)) {
188 newWays.remove(way);
189 }
190 }
191 return newWays;
192 }
193
194 public static String invertNumber(String value) {
195 Pattern pattern = Pattern.compile("^([+-]?)(\\d.*)$", Pattern.CASE_INSENSITIVE);
196 Matcher matcher = pattern.matcher(value);
197 if (!matcher.matches()) return value;
198 String sign = matcher.group(1);
199 String rest = matcher.group(2);
200 sign = "-".equals(sign) ? "" : "-";
201 return sign + rest;
202 }
203
204 @Override
205 public Collection<Command> execute(Way oldway, Way way) throws UserCancelException {
206 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap = new HashMap<>();
207
208 List<TagCorrection> tagCorrections = new ArrayList<>();
209 for (String key : way.keySet()) {
210 String value = way.get(key);
211 Tag newTag = TagSwitcher.apply(key, value);
212 String newKey = newTag.getKey();
213 String newValue = newTag.getValue();
214
215 boolean needsCorrection = !key.equals(newKey);
216 if (way.get(newKey) != null && way.get(newKey).equals(newValue)) {
217 needsCorrection = false;
218 }
219 if (!value.equals(newValue)) {
220 needsCorrection = true;
221 }
222
223 if (needsCorrection) {
224 tagCorrections.add(new TagCorrection(key, value, newKey, newValue));
225 }
226 }
227 if (!tagCorrections.isEmpty()) {
228 tagCorrectionsMap.put(way, tagCorrections);
229 }
230
231 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap = new HashMap<>();
232 List<RoleCorrection> roleCorrections = new ArrayList<>();
233
234 Collection<OsmPrimitive> referrers = oldway.getReferrers();
235 for (OsmPrimitive referrer: referrers) {
236 if (!(referrer instanceof Relation)) {
237 continue;
238 }
239 Relation relation = (Relation) referrer;
240 int position = 0;
241 for (RelationMember member : relation.getMembers()) {
242 if (!member.getMember().hasEqualSemanticAttributes(oldway)
243 || !member.hasRole()) {
244 position++;
245 continue;
246 }
247
248 boolean found = false;
249 String newRole = null;
250 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
251 newRole = prefixSuffixSwitcher.apply(member.getRole());
252 if (!newRole.equals(member.getRole())) {
253 found = true;
254 break;
255 }
256 }
257
258 if (found) {
259 roleCorrections.add(new RoleCorrection(relation, position, member, newRole));
260 }
261
262 position++;
263 }
264 }
265 if (!roleCorrections.isEmpty()) {
266 roleCorrectionMap.put(way, roleCorrections);
267 }
268
269 return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
270 tr("When reversing this way, the following changes are suggested in order to maintain data consistency."));
271 }
272
273 private static boolean ignoreKeyForCorrection(String key) {
274 for (Pattern ignoredKey : ignoredKeys) {
275 if (ignoredKey.matcher(key).matches()) {
276 return true;
277 }
278 }
279 return false;
280 }
281}
Note: See TracBrowser for help on using the repository browser.