source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorter.java@ 12537

Last change on this file since 12537 was 12537, checked in by Don-vip, 7 years ago

PMD - VariableNamingConventions

  • Property svn:eol-style set to native
File size: 8.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation.sort;
3
4import java.util.ArrayList;
5import java.util.Arrays;
6import java.util.Collection;
7import java.util.HashMap;
8import java.util.LinkedHashMap;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.Map;
12import java.util.Map.Entry;
13
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.Relation;
16import org.openstreetmap.josm.data.osm.RelationMember;
17import org.openstreetmap.josm.gui.DefaultNameFormatter;
18import org.openstreetmap.josm.tools.AlphanumComparator;
19import org.openstreetmap.josm.tools.Utils;
20
21public class RelationSorter {
22
23 private interface AdditionalSorter {
24 boolean acceptsMember(RelationMember m);
25
26 List<RelationMember> sortMembers(List<RelationMember> list);
27 }
28
29 private static final Collection<AdditionalSorter> ADDITIONAL_SORTERS = Arrays.asList(
30 // first adequate sorter is used, so order matters
31 new AssociatedStreetRoleStreetSorter(),
32 new AssociatedStreetRoleAddressHouseSorter(),
33 new PublicTransportRoleStopPlatformSorter()
34 );
35
36 /**
37 * Class that sorts the {@code street} members of
38 * {@code type=associatedStreet} and {@code type=street} relations.
39 */
40 private static class AssociatedStreetRoleStreetSorter implements AdditionalSorter {
41
42 @Override
43 public boolean acceptsMember(RelationMember m) {
44 return "street".equals(m.getRole());
45 }
46
47 @Override
48 public List<RelationMember> sortMembers(List<RelationMember> list) {
49 return sortMembersByConnectivity(list);
50 }
51 }
52
53 /**
54 * Class that sorts the {@code address} and {@code house} members of
55 * {@code type=associatedStreet} and {@code type=street} relations.
56 */
57 private static class AssociatedStreetRoleAddressHouseSorter implements AdditionalSorter {
58
59 @Override
60 public boolean acceptsMember(RelationMember m) {
61 return "address".equals(m.getRole()) || "house".equals(m.getRole());
62 }
63
64 @Override
65 public List<RelationMember> sortMembers(List<RelationMember> list) {
66 list.sort((a, b) -> {
67 final int houseNumber = AlphanumComparator.getInstance().compare(
68 a.getMember().get("addr:housenumber"),
69 b.getMember().get("addr:housenumber"));
70 if (houseNumber != 0) {
71 return houseNumber;
72 }
73 final String aDisplayName = a.getMember().getDisplayName(DefaultNameFormatter.getInstance());
74 final String bDisplayName = b.getMember().getDisplayName(DefaultNameFormatter.getInstance());
75 return AlphanumComparator.getInstance().compare(aDisplayName, bDisplayName);
76 });
77 return list;
78 }
79 }
80
81 /**
82 * Class that sorts the {@code platform} and {@code stop} members of
83 * {@code type=public_transport} relations.
84 */
85 private static class PublicTransportRoleStopPlatformSorter implements AdditionalSorter {
86
87 @Override
88 public boolean acceptsMember(RelationMember m) {
89 return m.getRole() != null && (m.getRole().startsWith("platform") || m.getRole().startsWith("stop"));
90 }
91
92 private static String getStopName(OsmPrimitive p) {
93 for (Relation ref : Utils.filteredCollection(p.getReferrers(), Relation.class)) {
94 if (ref.hasTag("type", "public_transport") && ref.hasTag("public_transport", "stop_area") && ref.getName() != null) {
95 return ref.getName();
96 }
97 }
98 return p.getName();
99 }
100
101 @Override
102 public List<RelationMember> sortMembers(List<RelationMember> list) {
103 final Map<String, RelationMember> platformByName = new HashMap<>();
104 for (RelationMember i : list) {
105 if (i.getRole().startsWith("platform")) {
106 final RelationMember old = platformByName.put(getStopName(i.getMember()), i);
107 if (old != null) {
108 // Platform with same name present. Stop to avoid damaging complicated relations.
109 // This case can happily be handled differently.
110 return list;
111 }
112 }
113 }
114 final List<RelationMember> sorted = new ArrayList<>(list.size());
115 for (RelationMember i : list) {
116 if (i.getRole().startsWith("stop")) {
117 sorted.add(i);
118 final RelationMember platform = platformByName.remove(getStopName(i.getMember()));
119 if (platform != null) {
120 sorted.add(platform);
121 }
122 }
123 }
124 sorted.addAll(platformByName.values());
125 return sorted;
126 }
127 }
128
129 /**
130 * Sort a collection of relation members by the way they are linked.
131 *
132 * @param relationMembers collection of relation members
133 * @return sorted collection of relation members
134 */
135 public List<RelationMember> sortMembers(List<RelationMember> relationMembers) {
136 List<RelationMember> newMembers = new ArrayList<>();
137
138 // Sort members with custom mechanisms (relation-dependent)
139 List<RelationMember> defaultMembers = new ArrayList<>(relationMembers.size());
140 // Maps sorter to assigned members for sorting. Use LinkedHashMap to retain order.
141 Map<AdditionalSorter, List<RelationMember>> customMap = new LinkedHashMap<>();
142
143 // Dispatch members to the first adequate sorter
144 for (RelationMember m : relationMembers) {
145 boolean wasAdded = false;
146 for (AdditionalSorter sorter : ADDITIONAL_SORTERS) {
147 if (sorter.acceptsMember(m)) {
148 List<RelationMember> list;
149 list = customMap.get(sorter);
150 if (list == null) {
151 list = new LinkedList<>();
152 customMap.put(sorter, list);
153 }
154 list.add(m);
155 wasAdded = true;
156 break;
157 }
158 }
159 if (!wasAdded) {
160 defaultMembers.add(m);
161 }
162 }
163
164 // Sort members and add them to result
165 for (Entry<AdditionalSorter, List<RelationMember>> entry : customMap.entrySet()) {
166 newMembers.addAll(entry.getKey().sortMembers(entry.getValue()));
167 }
168 newMembers.addAll(sortMembersByConnectivity(defaultMembers));
169 return newMembers;
170 }
171
172 public static List<RelationMember> sortMembersByConnectivity(List<RelationMember> defaultMembers) {
173
174 List<RelationMember> newMembers = new ArrayList<>();
175
176 RelationNodeMap map = new RelationNodeMap(defaultMembers);
177 // List of groups of linked members
178 //
179 List<LinkedList<Integer>> allGroups = new ArrayList<>();
180
181 // current group of members that are linked among each other
182 // Two successive members are always linked i.e. have a common node.
183 //
184 LinkedList<Integer> group;
185
186 Integer first;
187 while ((first = map.pop()) != null) {
188 group = new LinkedList<>();
189 group.add(first);
190
191 allGroups.add(group);
192
193 Integer next = first;
194 while ((next = map.popAdjacent(next)) != null) {
195 group.addLast(next);
196 }
197
198 // The first element need not be in front of the list.
199 // So the search goes in both directions
200 //
201 next = first;
202 while ((next = map.popAdjacent(next)) != null) {
203 group.addFirst(next);
204 }
205 }
206
207 for (List<Integer> tmpGroup : allGroups) {
208 for (Integer p : tmpGroup) {
209 newMembers.add(defaultMembers.get(p));
210 }
211 }
212
213 // Finally, add members that have not been sorted at all
214 for (Integer i : map.getNotSortableMembers()) {
215 newMembers.add(defaultMembers.get(i));
216 }
217
218 return newMembers;
219 }
220
221}
Note: See TracBrowser for help on using the repository browser.