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

Last change on this file since 5630 was 5630, checked in by jttt, 11 years ago

Relation sorting and way connection refactored, added tests

File size: 5.3 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.HashMap;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.Map;
12
13import org.openstreetmap.josm.data.osm.RelationMember;
14
15public class RelationSorter {
16
17 private static interface AdditionalSorter {
18 public boolean acceptsMember(RelationMember m);
19 public List<RelationMember> sortMembers(List<RelationMember> list);
20 }
21
22 private static final Collection<AdditionalSorter> additionalSorters = new ArrayList<AdditionalSorter>();
23
24 static {
25 additionalSorters.add(new AssociatedStreetSorter());
26 }
27
28 /**
29 * Class that sorts type=associatedStreet relation's houses.
30 */
31 private static class AssociatedStreetSorter implements AdditionalSorter {
32
33 @Override
34 public boolean acceptsMember(RelationMember m) {
35 return m != null
36 && m.getRole() != null && m.getRole().equals("house")
37 && m.getMember() != null && m.getMember().get("addr:housenumber") != null;
38 }
39
40 @Override
41 public List<RelationMember> sortMembers(List<RelationMember> list) {
42 Collections.sort(list, new Comparator<RelationMember>() {
43 @Override
44 public int compare(RelationMember a, RelationMember b) {
45 if (a == b || a.getMember() == b.getMember()) return 0;
46 String addrA = a.getMember().get("addr:housenumber").trim();
47 String addrB = b.getMember().get("addr:housenumber").trim();
48 if (addrA.equals(addrB)) return 0;
49 // Strip non-digits (from "1B" addresses for example)
50 String addrAnum = addrA.replaceAll("\\D+", "");
51 String addrBnum = addrB.replaceAll("\\D+", "");
52 // Compare only numbers
53 try {
54 Integer res = Integer.parseInt(addrAnum) - Integer.parseInt(addrBnum);
55 if (res != 0) return res;
56 } catch (NumberFormatException e) {
57 // Ignore NumberFormatException. If the number is not composed of digits, strings are compared next
58 }
59 // Same number ? Compare full strings
60 return addrA.compareTo(addrB);
61 }
62 });
63 return list;
64 }
65 }
66
67 /*
68 * Sort a collection of relation members by the way they are linked.
69 *
70 * @param relationMembers collection of relation members
71 * @return sorted collection of relation members
72 */
73 public List<RelationMember> sortMembers(List<RelationMember> relationMembers) {
74 ArrayList<RelationMember> newMembers = new ArrayList<RelationMember>();
75
76 // Sort members with custom mechanisms (relation-dependent)
77 List<RelationMember> defaultMembers = new ArrayList<RelationMember>(relationMembers.size());
78 Map<AdditionalSorter, List<RelationMember>> customMap = new HashMap<AdditionalSorter, List<RelationMember>>();
79
80 // Dispatch members to correct sorters
81 for (RelationMember m : relationMembers) {
82 for (AdditionalSorter sorter : additionalSorters) {
83 List<RelationMember> list = defaultMembers;
84 if (sorter.acceptsMember(m)) {
85 list = customMap.get(sorter);
86 if (list == null) {
87 customMap.put(sorter, list = new LinkedList<RelationMember>());
88 }
89 }
90 list.add(m);
91 }
92 }
93
94 // Sort members and add them to result
95 for (AdditionalSorter s : customMap.keySet()) {
96 newMembers.addAll(s.sortMembers(customMap.get(s)));
97 }
98
99 RelationNodeMap map = new RelationNodeMap(defaultMembers);
100 // List of groups of linked members
101 //
102 List<LinkedList<Integer>> allGroups = new ArrayList<LinkedList<Integer>>();
103
104 // current group of members that are linked among each other
105 // Two successive members are always linked i.e. have a common node.
106 //
107 LinkedList<Integer> group;
108
109 Integer first;
110 while ((first = map.pop()) != null) {
111 group = new LinkedList<Integer>();
112 group.add(first);
113
114 allGroups.add(group);
115
116 Integer next = first;
117 while ((next = map.popAdjacent(next)) != null) {
118 group.addLast(next);
119 }
120
121 // The first element need not be in front of the list.
122 // So the search goes in both directions
123 //
124 next = first;
125 while ((next = map.popAdjacent(next)) != null) {
126 group.addFirst(next);
127 }
128 }
129
130 for (LinkedList<Integer> tmpGroup : allGroups) {
131 for (Integer p : tmpGroup) {
132 newMembers.add(defaultMembers.get(p));
133 }
134 }
135
136 // Finally, add members that have not been sorted at all
137 for (Integer i : map.getNotSortableMembers()) {
138 newMembers.add(defaultMembers.get(i));
139 }
140
141 return newMembers;
142 }
143
144}
Note: See TracBrowser for help on using the repository browser.