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

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

see #15182 - move NameFormatter* from gui to data.osm

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