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

Last change on this file since 5732 was 5721, checked in by Don-vip, 11 years ago

fix #8381 - no warning about left / right when combining ways and changing the direction

  • Property svn:eol-style set to native
File size: 8.4 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.Arrays;
8import java.util.Collection;
9import java.util.HashMap;
10import java.util.List;
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.Way;
21
22/**
23 * A ReverseWayTagCorrector handles necessary corrections of tags
24 * when a way is reversed. E.g. oneway=yes needs to be changed
25 * to oneway=-1 and vice versa.
26 *
27 * The Corrector offers the automatic resolution in an dialog
28 * for the user to confirm.
29 */
30
31public class ReverseWayTagCorrector extends TagCorrector<Way> {
32
33 private static class PrefixSuffixSwitcher {
34
35 private static final String SEPARATOR = "[:_]?";
36
37 private final String a;
38 private final String b;
39 private final Pattern startPattern;
40 private final Pattern endPattern;
41
42 public PrefixSuffixSwitcher(String a, String b) {
43 this.a = a;
44 this.b = b;
45 startPattern = Pattern.compile(
46 "^(" + a + "|" + b + ")(" + SEPARATOR + "|$)",
47 Pattern.CASE_INSENSITIVE);
48 endPattern = Pattern.compile("^.*" +
49 SEPARATOR + "(" + a + "|" + b + ")$",
50 Pattern.CASE_INSENSITIVE);
51 }
52
53 public String apply(String text) {
54 Matcher m = startPattern.matcher(text);
55 if (!m.lookingAt()) {
56 m = endPattern.matcher(text);
57 }
58
59 if (m.lookingAt()) {
60 String leftRight = m.group(1).toLowerCase();
61
62 StringBuilder result = new StringBuilder();
63 result.append(text.substring(0, m.start(1)));
64 result.append(leftRight.equals(a) ? b : a);
65 result.append(text.substring(m.end(1)));
66
67 return result.toString();
68 }
69 return text;
70 }
71 }
72
73 private static final PrefixSuffixSwitcher[] prefixSuffixSwitchers =
74 new PrefixSuffixSwitcher[] {
75 new PrefixSuffixSwitcher("left", "right"),
76 new PrefixSuffixSwitcher("forward", "backward"),
77 new PrefixSuffixSwitcher("forwards", "backwards"),
78 new PrefixSuffixSwitcher("up", "down"),
79 new PrefixSuffixSwitcher("east", "west"),
80 new PrefixSuffixSwitcher("north", "south"),
81 };
82
83 /**
84 * Tests whether way can be reversed without semantic change, i.e., whether tags have to be changed.
85 * Looks for keys like oneway, oneway:bicycle, cycleway:right:oneway, left/right.
86 * @param way
87 * @return false if tags should be changed to keep semantic, true otherwise.
88 */
89 public static boolean isReversible(Way way) {
90 for (String key : way.keySet()) {
91 for (String k : Arrays.asList("oneway", "incline", "direction")) {
92 if (key.startsWith(k) || key.endsWith(k)) {
93 return false;
94 }
95 }
96 String value = way.get(key);
97 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
98 if (!key.equals(prefixSuffixSwitcher.apply(key)) || !value.equals(prefixSuffixSwitcher.apply(value))) {
99 return false;
100 }
101 }
102 }
103 return true;
104 }
105
106 public static List<Way> irreversibleWays(List<Way> ways) {
107 List<Way> newWays = new ArrayList<Way>(ways);
108 for (Way way : ways) {
109 if (isReversible(way)) {
110 newWays.remove(way);
111 }
112 }
113 return newWays;
114 }
115
116 public String invertNumber(String value) {
117 Pattern pattern = Pattern.compile("^([+-]?)(\\d.*)$", Pattern.CASE_INSENSITIVE);
118 Matcher matcher = pattern.matcher(value);
119 if (!matcher.matches()) return value;
120 String sign = matcher.group(1);
121 String rest = matcher.group(2);
122 sign = sign.equals("-") ? "" : "-";
123 return sign + rest;
124 }
125
126 @Override
127 public Collection<Command> execute(Way oldway, Way way) throws UserCancelException {
128 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap =
129 new HashMap<OsmPrimitive, List<TagCorrection>>();
130
131 ArrayList<TagCorrection> tagCorrections = new ArrayList<TagCorrection>();
132 for (String key : way.keySet()) {
133 String newKey = key;
134 String value = way.get(key);
135 String newValue = value;
136
137 if (key.startsWith("oneway") || key.endsWith("oneway")) {
138 if (OsmUtils.isReversed(value)) {
139 newValue = OsmUtils.trueval;
140 } else if (OsmUtils.isTrue(value)) {
141 newValue = OsmUtils.reverseval;
142 }
143 } else if (key.startsWith("incline") || key.endsWith("incline")
144 || key.startsWith("direction") || key.endsWith("direction")) {
145 PrefixSuffixSwitcher switcher = new PrefixSuffixSwitcher("up", "down");
146 newValue = switcher.apply(value);
147 if (newValue.equals(value)) {
148 newValue = invertNumber(value);
149 }
150 } else if (!ignoreKeyForPrefixSuffixCorrection(key)) {
151 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
152 newKey = prefixSuffixSwitcher.apply(key);
153 if (!key.equals(newKey)) {
154 break;
155 }
156 newValue = prefixSuffixSwitcher.apply(value);
157 if (!value.equals(newValue)) {
158 break;
159 }
160 }
161 }
162
163 boolean needsCorrection = !key.equals(newKey);
164 if (way.get(newKey) != null && way.get(newKey).equals(newValue)) {
165 needsCorrection = false;
166 }
167 if (!value.equals(newValue)) {
168 needsCorrection = true;
169 }
170
171 if (needsCorrection) {
172 tagCorrections.add(new TagCorrection(key, value, newKey, newValue));
173 }
174 }
175 if (!tagCorrections.isEmpty()) {
176 tagCorrectionsMap.put(way, tagCorrections);
177 }
178
179 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap =
180 new HashMap<OsmPrimitive, List<RoleCorrection>>();
181 ArrayList<RoleCorrection> roleCorrections = new ArrayList<RoleCorrection>();
182
183 Collection<OsmPrimitive> referrers = oldway.getReferrers();
184 for (OsmPrimitive referrer: referrers) {
185 if (! (referrer instanceof Relation)) {
186 continue;
187 }
188 Relation relation = (Relation)referrer;
189 int position = 0;
190 for (RelationMember member : relation.getMembers()) {
191 if (!member.getMember().hasEqualSemanticAttributes(oldway)
192 || !member.hasRole()) {
193 position++;
194 continue;
195 }
196
197 boolean found = false;
198 String newRole = null;
199 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
200 newRole = prefixSuffixSwitcher.apply(member.getRole());
201 if (!newRole.equals(member.getRole())) {
202 found = true;
203 break;
204 }
205 }
206
207 if (found) {
208 roleCorrections.add(new RoleCorrection(relation, position, member, newRole));
209 }
210
211 position++;
212 }
213 }
214 if (!roleCorrections.isEmpty()) {
215 roleCorrectionMap.put(way, roleCorrections);
216 }
217
218 return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
219 tr("When reversing this way, the following changes to properties "
220 + "of the way and its nodes are suggested in order "
221 + "to maintain data consistency."));
222 }
223
224 private static boolean ignoreKeyForPrefixSuffixCorrection(String key) {
225 return key.contains("name") || key.equals("tiger:county")
226 || key.equalsIgnoreCase("fixme") || key.startsWith("note");
227 }
228}
Note: See TracBrowser for help on using the repository browser.