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

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

checkstyle: redundant modifiers

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