source: josm/trunk/src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java@ 13957

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

use IRelation in RelationListDialog and *RelationActions

  • Property svn:eol-style set to native
File size: 9.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions.relation;
3
4import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.FROM_FIRST_MEMBER;
5import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.TO_FILE;
6import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.TO_LAYER;
7import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
8import static org.openstreetmap.josm.tools.I18n.tr;
9
10import java.awt.event.ActionEvent;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.EnumSet;
16import java.util.HashMap;
17import java.util.Iterator;
18import java.util.List;
19import java.util.ListIterator;
20import java.util.Map;
21import java.util.Set;
22import java.util.Stack;
23
24import org.openstreetmap.josm.actions.GpxExportAction;
25import org.openstreetmap.josm.actions.IPrimitiveAction;
26import org.openstreetmap.josm.data.gpx.GpxData;
27import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
28import org.openstreetmap.josm.data.gpx.WayPoint;
29import org.openstreetmap.josm.data.osm.IPrimitive;
30import org.openstreetmap.josm.data.osm.Node;
31import org.openstreetmap.josm.data.osm.Relation;
32import org.openstreetmap.josm.data.osm.RelationMember;
33import org.openstreetmap.josm.data.osm.Way;
34import org.openstreetmap.josm.gui.MainApplication;
35import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType;
36import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionTypeCalculator;
37import org.openstreetmap.josm.gui.layer.GpxLayer;
38import org.openstreetmap.josm.gui.layer.Layer;
39import org.openstreetmap.josm.gui.layer.OsmDataLayer;
40import org.openstreetmap.josm.tools.SubclassFilteredCollection;
41
42/**
43 * Exports the current relation to a single GPX track,
44 * currently for type=route and type=superroute relations only.
45 *
46 * @since 13210
47 */
48public class ExportRelationToGpxAction extends GpxExportAction
49 implements IPrimitiveAction {
50
51 /** Enumeration of export variants */
52 public enum Mode {
53 /** concatenate members from first to last element */
54 FROM_FIRST_MEMBER,
55 /** concatenate members from last to first element */
56 FROM_LAST_MEMBER,
57 /** export to GPX layer and add to LayerManager */
58 TO_LAYER,
59 /** export to GPX file and open FileChooser */
60 TO_FILE
61 }
62
63 /** Mode of this ExportToGpxAction */
64 protected final Set<Mode> mode;
65
66 /** Primitives this action works on */
67 protected Collection<Relation> relations = Collections.<Relation>emptySet();
68
69 /** Construct a new ExportRelationToGpxAction with default mode */
70 public ExportRelationToGpxAction() {
71 this(EnumSet.of(FROM_FIRST_MEMBER, TO_FILE));
72 }
73
74 /**
75 * Constructs a new {@code ExportRelationToGpxAction}
76 *
77 * @param mode which mode to use, see {@code ExportRelationToGpxAction.Mode}
78 */
79 public ExportRelationToGpxAction(Set<Mode> mode) {
80 super(name(mode), mode.contains(TO_FILE) ? "exportgpx" : "dialogs/layerlist", tooltip(mode),
81 null, false, null, false);
82 putValue("help", ht("/Action/ExportRelationToGpx"));
83 this.mode = mode;
84 }
85
86 private static String name(Set<Mode> mode) {
87 if (mode.contains(TO_FILE)) {
88 if (mode.contains(FROM_FIRST_MEMBER)) {
89 return tr("Export GPX file starting from first member");
90 } else {
91 return tr("Export GPX file starting from last member");
92 }
93 } else {
94 if (mode.contains(FROM_FIRST_MEMBER)) {
95 return tr("Convert to GPX layer starting from first member");
96 } else {
97 return tr("Convert to GPX layer starting from last member");
98 }
99 }
100 }
101
102 private static String tooltip(Set<Mode> mode) {
103 if (mode.contains(FROM_FIRST_MEMBER)) {
104 return tr("Flatten this relation to a single gpx track recursively, " +
105 "starting with the first member, successively continuing to the last.");
106 } else {
107 return tr("Flatten this relation to a single gpx track recursively, " +
108 "starting with the last member, successively continuing to the first.");
109 }
110 }
111
112 private static final class BidiIterableList {
113 private final List<RelationMember> l;
114
115 private BidiIterableList(List<RelationMember> l) {
116 this.l = l;
117 }
118
119 public Iterator<RelationMember> iterator() {
120 return l.iterator();
121 }
122
123 public Iterator<RelationMember> reverseIterator() {
124 ListIterator<RelationMember> li = l.listIterator(l.size());
125 return new Iterator<RelationMember>() {
126 @Override
127 public boolean hasNext() {
128 return li.hasPrevious();
129 }
130
131 @Override
132 public RelationMember next() {
133 return li.previous();
134 }
135
136 @Override
137 public void remove() {
138 li.remove();
139 }
140 };
141 }
142 }
143
144 @Override
145 protected Layer getLayer() {
146 List<RelationMember> flat = new ArrayList<>();
147
148 List<RelationMember> init = new ArrayList<>();
149 relations.forEach(t -> init.add(new RelationMember("", t)));
150 BidiIterableList l = new BidiIterableList(init);
151
152 Stack<Iterator<RelationMember>> stack = new Stack<>();
153 stack.push(mode.contains(FROM_FIRST_MEMBER) ? l.iterator() : l.reverseIterator());
154
155 List<Relation> relsFound = new ArrayList<>();
156 do {
157 Iterator<RelationMember> i = stack.peek();
158 if (!i.hasNext())
159 stack.pop();
160 while (i.hasNext()) {
161 RelationMember m = i.next();
162 if (m.isRelation() && !m.getRelation().isIncomplete()) {
163 l = new BidiIterableList(m.getRelation().getMembers());
164 stack.push(mode.contains(FROM_FIRST_MEMBER) ? l.iterator() : l.reverseIterator());
165 relsFound.add(m.getRelation());
166 break;
167 }
168 if (m.isWay()) {
169 flat.add(m);
170 }
171 }
172 } while (!stack.isEmpty());
173
174 GpxData gpxData = new GpxData();
175 String layerName = " (GPX export)";
176 long time = System.currentTimeMillis()-24*3600*1000;
177
178 if (!flat.isEmpty()) {
179 Map<String, Object> trkAttr = new HashMap<>();
180 Collection<Collection<WayPoint>> trk = new ArrayList<>();
181 List<WayPoint> trkseg = new ArrayList<>();
182 trk.add(trkseg);
183
184 List<WayConnectionType> wct = new WayConnectionTypeCalculator().updateLinks(flat);
185 final HashMap<String, Integer> names = new HashMap<>();
186 for (int i = 0; i < flat.size(); i++) {
187 if (!wct.get(i).isOnewayLoopBackwardPart) {
188 if (!wct.get(i).direction.isRoundabout()) {
189 if (!wct.get(i).linkPrev && !trkseg.isEmpty()) {
190 gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));
191 trkAttr.clear();
192 trk.clear();
193 trkseg.clear();
194 trk.add(trkseg);
195 }
196 if (trkAttr.isEmpty()) {
197 Relation r = Way.getParentRelations(Arrays.asList(flat.get(i).getWay()))
198 .stream().filter(relsFound::contains).findFirst().orElseGet(null);
199 if (r != null)
200 trkAttr.put("name", r.getName() != null ? r.getName() : r.getId());
201 GpxData.ensureUniqueName(trkAttr, names);
202 }
203 List<Node> ln = flat.get(i).getWay().getNodes();
204 if (wct.get(i).direction == WayConnectionType.Direction.BACKWARD)
205 Collections.reverse(ln);
206 for (Node n: ln) {
207 trkseg.add(OsmDataLayer.nodeToWayPoint(n, time));
208 time += 1000;
209 }
210 }
211 }
212 }
213 gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));
214
215 String lprefix = relations.iterator().next().getName();
216 if (lprefix == null || relations.size() > 1)
217 lprefix = tr("Selected Relations");
218 layerName = lprefix + layerName;
219 }
220
221 return new GpxLayer(gpxData, layerName, true);
222 }
223
224 /**
225 *
226 * @param e the ActionEvent
227 */
228 @Override
229 public void actionPerformed(ActionEvent e) {
230 if (mode.contains(TO_LAYER))
231 MainApplication.getLayerManager().addLayer(getLayer());
232 if (mode.contains(TO_FILE))
233 super.actionPerformed(e);
234 }
235
236 @Override
237 public void setPrimitives(Collection<? extends IPrimitive> primitives) {
238 relations = Collections.<Relation>emptySet();
239 if (primitives != null && !primitives.isEmpty()) {
240 relations = new SubclassFilteredCollection<>(primitives,
241 r -> r instanceof Relation && r.hasTag("type", Arrays.asList("route", "superroute")));
242 }
243 updateEnabledState();
244 }
245
246 @Override
247 protected void updateEnabledState() {
248 setEnabled(!relations.isEmpty());
249 }
250}
Note: See TracBrowser for help on using the repository browser.