source: osm/applications/editors/josm/plugins/reltoolbox/src/relcontext/actions/ReconstructRouteAction.java@ 36217

Last change on this file since 36217 was 36217, checked in by GerdP, 22 months ago

fix #23521: fix some memory leaks

  • dispose dialogs
  • either avoid to create clones of ways or relations or use setNodes(null) / setMembers(null)
  • replaces most ChangeCommand instances by better specialized alternatives
  • add some comments
  • fix some checkstyle / sonar issues
  • Property svn:eol-style set to native
File size: 9.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package relcontext.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.util.ArrayList;
8import java.util.LinkedHashMap;
9import java.util.List;
10import java.util.Map;
11import java.util.Objects;
12
13import javax.swing.AbstractAction;
14
15import org.openstreetmap.josm.command.ChangeMembersCommand;
16import org.openstreetmap.josm.data.UndoRedoHandler;
17import org.openstreetmap.josm.data.coor.EastNorth;
18import org.openstreetmap.josm.data.osm.Node;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
21import org.openstreetmap.josm.data.osm.Relation;
22import org.openstreetmap.josm.data.osm.RelationMember;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.gui.dialogs.relation.sort.RelationSorter;
25import org.openstreetmap.josm.tools.Geometry;
26import org.openstreetmap.josm.tools.ImageProvider;
27
28import relcontext.ChosenRelation;
29import relcontext.ChosenRelationListener;
30
31/**
32 * Build in order stop/platforms, stop/platforms ... route
33 * @author freeExec
34 */
35public class ReconstructRouteAction extends AbstractAction implements ChosenRelationListener {
36 private final transient ChosenRelation rel;
37
38 /**
39 * Reconstruct route relation to scheme of public_transport.
40 * @param rel chosen relation
41 */
42 public ReconstructRouteAction(ChosenRelation rel) {
43 super(tr("Reconstruct route"));
44 putValue(SMALL_ICON, ImageProvider.get("dialogs", "filter"));
45 putValue(LONG_DESCRIPTION, "Reconstruct route relation to scheme of public_transport");
46 this.rel = rel;
47 rel.addChosenRelationListener(this);
48 setEnabled(isSuitableRelation(rel.get()));
49 }
50
51 @Override
52 public void actionPerformed(ActionEvent e) {
53 Relation r = rel.get();
54 List<RelationMember> recMembers = new ArrayList<>();
55
56 Map<OsmPrimitive, RelationMember> stopMembers = new LinkedHashMap<>();
57 Map<String, List<RelationMember>> platformMembers = new LinkedHashMap<>();
58
59 List<RelationMember> routeMembers = new ArrayList<>();
60 List<RelationMember> wtfMembers = new ArrayList<>();
61
62 int mCount = r.getMembersCount();
63 for (int i = 0; i < mCount; i++) {
64 RelationMember m = r.getMember(i);
65 if (PublicTransportHelper.isMemberStop(m)) {
66 RelationMember rm = new RelationMember(
67 m.hasRole() ? m.getRole() : PublicTransportHelper.STOP,
68 m.getMember());
69 stopMembers.put(rm.getMember(), rm);
70 } else if (PublicTransportHelper.isMemberPlatform(m)) {
71 RelationMember rm = new RelationMember(
72 m.hasRole() ? m.getRole() : PublicTransportHelper.PLATFORM,
73 m.getMember());
74 String platformName = PublicTransportHelper.getNameViaStoparea(rm);
75 if (platformName == null) {
76 platformName = "";
77 }
78 if (platformMembers.containsKey(platformName)) {
79 platformMembers.get(platformName).add(rm);
80 } else {
81 List<RelationMember> nList = new ArrayList<>();
82 nList.add(rm);
83 platformMembers.put(platformName, nList);
84 }
85 } else if (PublicTransportHelper.isMemberRouteway(m)) {
86 routeMembers.add(new RelationMember(m));
87 } else {
88 wtfMembers.add(new RelationMember(m));
89 }
90 }
91
92 routeMembers = RelationSorter.sortMembersByConnectivity(routeMembers);
93
94 Node lastNode = null;
95 for (int rIndex = 0; rIndex < routeMembers.size(); rIndex++) {
96 Way w = (Way) routeMembers.get(rIndex).getMember();
97 boolean dirForward = false;
98 if (lastNode == null) { // first segment
99 if (routeMembers.size() > 2) {
100 Way nextWay = (Way) routeMembers.get(rIndex + 1).getMember();
101 if (Objects.equals(w.lastNode(), nextWay.lastNode()) || Objects.equals(w.lastNode(), nextWay.firstNode())) {
102 dirForward = true;
103 lastNode = w.lastNode();
104 } else {
105 lastNode = w.firstNode();
106 }
107 } // else one segment - direction unknown
108 } else {
109 if (lastNode.equals(w.firstNode())) {
110 dirForward = true; lastNode = w.lastNode();
111 } else {
112 lastNode = w.firstNode();
113 }
114 }
115 final int wayNodeBeginIndex = (dirForward ? 0 : w.getNodesCount() - 1);
116 final int wayNodeEndIndex = (dirForward ? w.getNodesCount() - 1 : 0);
117 final int increment = (dirForward ? 1 : -1);
118 for (int nIndex = wayNodeBeginIndex;
119 nIndex != wayNodeEndIndex;
120 nIndex += increment) {
121 Node refNode = w.getNode(nIndex);
122 if (!(PublicTransportHelper.isNodeStop(refNode) && stopMembers.containsKey(refNode)))
123 continue;
124 recMembers.add(stopMembers.get(refNode));
125 stopMembers.remove(refNode);
126 String stopName = PublicTransportHelper.getNameViaStoparea(refNode);
127 if (stopName == null) {
128 stopName = "";
129 }
130 boolean existsPlatform = platformMembers.containsKey(stopName);
131 if (!existsPlatform) {
132 stopName = ""; // find of the nameless
133 }
134 if (existsPlatform || platformMembers.containsKey(stopName)) {
135 List<RelationMember> lMember = platformMembers.get(stopName);
136 if (lMember.size() == 1) {
137 recMembers.add(lMember.get(0));
138 lMember.remove(0);
139 } else {
140 // choose closest
141 RelationMember candidat = getClosestPlatform(lMember, refNode);
142 if (candidat != null) {
143 recMembers.add(candidat);
144 lMember.remove(candidat);
145 }
146 }
147 if (lMember.isEmpty()) {
148 platformMembers.remove(stopName);
149 }
150 }
151 }
152 }
153
154 for (RelationMember stop : stopMembers.values()) {
155 recMembers.add(stop);
156 String stopName = PublicTransportHelper.getNameViaStoparea(stop);
157 boolean existsPlatform = platformMembers.containsKey(stopName);
158 if (!existsPlatform) {
159 stopName = ""; // find of the nameless
160 }
161 if (existsPlatform || platformMembers.containsKey(stopName)) {
162 List<RelationMember> lMember = platformMembers.get(stopName);
163 if (lMember.size() == 1) {
164 recMembers.add(lMember.get(0));
165 lMember.remove(0);
166 } else {
167 // choose closest
168 RelationMember candidat = getClosestPlatform(lMember, stop.getNode());
169 if (candidat != null) {
170 recMembers.add(candidat);
171 lMember.remove(candidat);
172 }
173 }
174 if (lMember.isEmpty()) {
175 platformMembers.remove(stopName);
176 }
177 }
178 }
179
180 for (List<RelationMember> lPlatforms : platformMembers.values()) {
181 for (RelationMember platform : lPlatforms) {
182 recMembers.add(platform);
183 }
184 }
185
186 for (RelationMember route : routeMembers) {
187 recMembers.add(route);
188 }
189 for (RelationMember wtf : wtfMembers) {
190 recMembers.add(wtf);
191 }
192 UndoRedoHandler.getInstance().add(new ChangeMembersCommand(r, recMembers));
193 }
194
195 private static final double maxSqrDistBetweenStopAndPlatform = 2000; // ~ 26m
196
197 private static RelationMember getClosestPlatform(List<RelationMember> members, Node stop) {
198 if (stop == null || members.isEmpty()) return null;
199 double maxDist = maxSqrDistBetweenStopAndPlatform;
200 RelationMember result = null;
201 for (RelationMember member : members) {
202 if (member.getType() == OsmPrimitiveType.NODE) {
203 Node node = member.getNode();
204 double sqrDist = stop.getEastNorth().distanceSq(node.getEastNorth());
205 if (sqrDist < maxDist) {
206 maxDist = sqrDist;
207 result = member;
208 }
209 } else if (member.getType() == OsmPrimitiveType.WAY) {
210 Way way = member.getWay();
211 EastNorth closest = Geometry.closestPointToSegment(
212 way.firstNode().getEastNorth(),
213 way.lastNode().getEastNorth(),
214 stop.getEastNorth()
215 );
216 double sqrDist = stop.getEastNorth().distanceSq(closest);
217 if (sqrDist < maxDist) {
218 maxDist = sqrDist;
219 result = member;
220 }
221 }
222 }
223 return result;
224 }
225
226 @Override
227 public void chosenRelationChanged(Relation oldRelation, Relation newRelation) {
228 setEnabled(isSuitableRelation(newRelation));
229 }
230
231 private static boolean isSuitableRelation(Relation newRelation) {
232 return !(newRelation == null || !"route".equals(newRelation.get("type")) || newRelation.getMembersCount() == 0);
233 }
234}
Note: See TracBrowser for help on using the repository browser.