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

Last change on this file since 6340 was 6326, checked in by Don-vip, 10 years ago

see #9172 - additional typo changes

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