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

Last change on this file since 6461 was 6461, checked in by simon04, 10 years ago

fix #9089 - improve sorting of associatedStreet/street relation members

In addition, fix a bug when more additionalSorters are adequate: This
led to duplicate relation members.

File size: 6.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.Collection;
6import java.util.Collections;
7import java.util.Comparator;
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.RelationMember;
15import org.openstreetmap.josm.gui.DefaultNameFormatter;
16import org.openstreetmap.josm.tools.AlphanumComparator;
17
18public class RelationSorter {
19
20 private static interface AdditionalSorter {
21 public boolean acceptsMember(RelationMember m);
22 public List<RelationMember> sortMembers(List<RelationMember> list);
23 }
24
25 private static final Collection<AdditionalSorter> additionalSorters = new ArrayList<AdditionalSorter>();
26
27 static {
28 // first adequate sorter is used, so order matters
29 additionalSorters.add(new AssociatedStreetRoleStreetSorter());
30 additionalSorters.add(new AssociatedStreetRoleAddressHouseSorter());
31 }
32
33 /**
34 * Class that sorts the {@code street} members of
35 * {@code type=associatedStreet} and {@code type=street} relations.
36 */
37 private static class AssociatedStreetRoleStreetSorter implements AdditionalSorter {
38
39 @Override
40 public boolean acceptsMember(RelationMember m) {
41 return "street".equals(m.getRole());
42 }
43
44 @Override
45 public List<RelationMember> sortMembers(List<RelationMember> list) {
46 return sortMembersByConnectivity(list);
47 }
48 }
49
50 /**
51 * Class that sorts the {@code address} and {@code house} members of
52 * {@code type=associatedStreet} and {@code type=street} relations.
53 */
54 private static class AssociatedStreetRoleAddressHouseSorter implements AdditionalSorter {
55
56 public static final AlphanumComparator ALPHANUM_COMPARATOR = new AlphanumComparator();
57
58 @Override
59 public boolean acceptsMember(RelationMember m) {
60 return "address".equals(m.getRole()) || "house".equals(m.getRole());
61 }
62
63 @Override
64 public List<RelationMember> sortMembers(List<RelationMember> list) {
65 Collections.sort(list, new Comparator<RelationMember>() {
66 @Override
67 public int compare(RelationMember a, RelationMember b) {
68 final int houseNumber = ALPHANUM_COMPARATOR.compare(
69 a.getMember().get("addr:housenumber"),
70 b.getMember().get("addr:housenumber"));
71 if (houseNumber != 0) {
72 return houseNumber;
73 }
74 final String aDisplayName = a.getMember().getDisplayName(DefaultNameFormatter.getInstance());
75 final String bDisplayName = b.getMember().getDisplayName(DefaultNameFormatter.getInstance());
76 return ALPHANUM_COMPARATOR.compare(aDisplayName, bDisplayName);
77 }
78 });
79 return list;
80 }
81 }
82
83 /**
84 * Sort a collection of relation members by the way they are linked.
85 *
86 * @param relationMembers collection of relation members
87 * @return sorted collection of relation members
88 */
89 public List<RelationMember> sortMembers(List<RelationMember> relationMembers) {
90 List<RelationMember> newMembers = new ArrayList<RelationMember>();
91
92 // Sort members with custom mechanisms (relation-dependent)
93 List<RelationMember> defaultMembers = new ArrayList<RelationMember>(relationMembers.size());
94 // Maps sorter to assigned members for sorting. Use LinkedHashMap to retain order.
95 Map<AdditionalSorter, List<RelationMember>> customMap = new LinkedHashMap<AdditionalSorter, List<RelationMember>>();
96
97 // Dispatch members to the first adequate sorter
98 for (RelationMember m : relationMembers) {
99 boolean wasAdded = false;
100 for (AdditionalSorter sorter : additionalSorters) {
101 if (sorter.acceptsMember(m)) {
102 List<RelationMember> list;
103 list = customMap.get(sorter);
104 if (list == null) {
105 customMap.put(sorter, list = new LinkedList<RelationMember>());
106 }
107 list.add(m);
108 wasAdded = true;
109 break;
110 }
111 }
112 if (!wasAdded) {
113 defaultMembers.add(m);
114 }
115 }
116
117 // Sort members and add them to result
118 for (Entry<AdditionalSorter, List<RelationMember>> entry : customMap.entrySet()) {
119 newMembers.addAll(entry.getKey().sortMembers(entry.getValue()));
120 }
121 newMembers.addAll(sortMembersByConnectivity(defaultMembers));
122 return newMembers;
123 }
124
125 public static List<RelationMember> sortMembersByConnectivity(List<RelationMember> defaultMembers) {
126
127 List<RelationMember> newMembers = new ArrayList<RelationMember>();
128
129 RelationNodeMap map = new RelationNodeMap(defaultMembers);
130 // List of groups of linked members
131 //
132 List<LinkedList<Integer>> allGroups = new ArrayList<LinkedList<Integer>>();
133
134 // current group of members that are linked among each other
135 // Two successive members are always linked i.e. have a common node.
136 //
137 LinkedList<Integer> group;
138
139 Integer first;
140 while ((first = map.pop()) != null) {
141 group = new LinkedList<Integer>();
142 group.add(first);
143
144 allGroups.add(group);
145
146 Integer next = first;
147 while ((next = map.popAdjacent(next)) != null) {
148 group.addLast(next);
149 }
150
151 // The first element need not be in front of the list.
152 // So the search goes in both directions
153 //
154 next = first;
155 while ((next = map.popAdjacent(next)) != null) {
156 group.addFirst(next);
157 }
158 }
159
160 for (LinkedList<Integer> tmpGroup : allGroups) {
161 for (Integer p : tmpGroup) {
162 newMembers.add(defaultMembers.get(p));
163 }
164 }
165
166 // Finally, add members that have not been sorted at all
167 for (Integer i : map.getNotSortableMembers()) {
168 newMembers.add(defaultMembers.get(i));
169 }
170
171 return newMembers;
172 }
173
174}
Note: See TracBrowser for help on using the repository browser.