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

Revision 4390, 7.9 KB checked in by simon04, 9 months ago (diff)

fix #5991, fix #6317 - improve automatic tag correction

  • Property svn:eol-style set to native
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 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    private static ArrayList<String> reversibleTags = new ArrayList<String>(
84            Arrays.asList(new String[] {"oneway", "incline", "direction"}));
85
86    public static boolean isReversible(Way way) {
87        for (String key : way.keySet()) {
88            if (reversibleTags.contains(key)) return false;
89            for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
90                if (!key.equals(prefixSuffixSwitcher.apply(key))) return false;
91            }
92        }
93
94        return true;
95    }
96
97    public static List<Way> irreversibleWays(List<Way> ways) {
98        List<Way> newWays = new ArrayList<Way>(ways);
99        for (Way way : ways) {
100            if (isReversible(way)) {
101                newWays.remove(way);
102            }
103        }
104        return newWays;
105    }
106
107    public String invertNumber(String value) {
108        Pattern pattern = Pattern.compile("^([+-]?)(\\d.*)$", Pattern.CASE_INSENSITIVE);
109        Matcher matcher = pattern.matcher(value);
110        if (!matcher.matches()) return value;
111        String sign = matcher.group(1);
112        String rest = matcher.group(2);
113        sign = sign.equals("-") ? "" : "-";
114        return sign + rest;
115    }
116
117    @Override
118    public Collection<Command> execute(Way oldway, Way way) throws UserCancelException {
119        Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap =
120            new HashMap<OsmPrimitive, List<TagCorrection>>();
121
122        ArrayList<TagCorrection> tagCorrections = new ArrayList<TagCorrection>();
123        for (String key : way.keySet()) {
124            String newKey = key;
125            String value = way.get(key);
126            String newValue = value;
127
128            if (key.equals("oneway")) {
129                if (OsmUtils.isReversed(value)) {
130                    newValue = OsmUtils.trueval;
131                } else if (OsmUtils.isTrue(value)) {
132                    newValue = OsmUtils.reverseval;
133                }
134            } else if (key.equals("incline") || key.equals("direction")) {
135                PrefixSuffixSwitcher switcher = new PrefixSuffixSwitcher("up", "down");
136                newValue = switcher.apply(value);
137                if (newValue.equals(value)) {
138                    newValue = invertNumber(value);
139                }
140            } else if (!ignoreKeyForPrefixSuffixCorrection(key)) {
141                for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
142                    newKey = prefixSuffixSwitcher.apply(key);
143                    if (!key.equals(newKey)) {
144                        break;
145                    }
146                    newValue = prefixSuffixSwitcher.apply(value);
147                    if (!value.equals(newValue)) {
148                        break;
149                    }
150                }
151            }
152
153            boolean needsCorrection = !key.equals(newKey);
154            if (way.get(newKey) != null && way.get(newKey).equals(newValue)) {
155                needsCorrection = false;
156            }
157            if (!value.equals(newValue)) {
158                needsCorrection = true;
159            }
160
161            if (needsCorrection) {
162                tagCorrections.add(new TagCorrection(key, value, newKey, newValue));
163            }
164        }
165        if (!tagCorrections.isEmpty()) {
166            tagCorrectionsMap.put(way, tagCorrections);
167        }
168
169        Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap =
170            new HashMap<OsmPrimitive, List<RoleCorrection>>();
171        ArrayList<RoleCorrection> roleCorrections = new ArrayList<RoleCorrection>();
172
173        Collection<OsmPrimitive> referrers = oldway.getReferrers();
174        for (OsmPrimitive referrer: referrers) {
175            if (! (referrer instanceof Relation)) {
176                continue;
177            }
178            Relation relation = (Relation)referrer;
179            int position = 0;
180            for (RelationMember member : relation.getMembers()) {
181                if (!member.getMember().hasEqualSemanticAttributes(oldway)
182                        || !member.hasRole()) {
183                    position++;
184                    continue;
185                }
186
187                boolean found = false;
188                String newRole = null;
189                for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
190                    newRole = prefixSuffixSwitcher.apply(member.getRole());
191                    if (!newRole.equals(member.getRole())) {
192                        found = true;
193                        break;
194                    }
195                }
196
197                if (found) {
198                    roleCorrections.add(new RoleCorrection(relation, position, member, newRole));
199                }
200
201                position++;
202            }
203        }
204        if (!roleCorrections.isEmpty()) {
205            roleCorrectionMap.put(way, roleCorrections);
206        }
207
208        return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
209                tr("When reversing this way, the following changes to properties "
210                        + "of the way and its nodes are suggested in order "
211                        + "to maintain data consistency."));
212    }
213
214    private static boolean ignoreKeyForPrefixSuffixCorrection(String key) {
215        return key.contains("name") || key.equals("tiger:county")
216                || key.equalsIgnoreCase("fixme") || key.startsWith("note");
217    }
218}
Note: See TracBrowser for help on using the repository browser.