source: josm/trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java@ 8041

Last change on this file since 8041 was 7518, checked in by Don-vip, 10 years ago

fix #10489 - Add additional attributes to GPX Export for nodes + GPX code improvements

  • Property svn:eol-style set to native
File size: 13.1 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.layer;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.Color;
9import java.awt.Dimension;
10import java.awt.Graphics2D;
11import java.io.File;
12import java.text.DateFormat;
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.Date;
17import java.util.LinkedList;
18import java.util.List;
19
20import javax.swing.Action;
21import javax.swing.Icon;
22import javax.swing.JScrollPane;
23import javax.swing.SwingUtilities;
24
25import org.openstreetmap.josm.Main;
26import org.openstreetmap.josm.actions.RenameLayerAction;
27import org.openstreetmap.josm.actions.SaveActionBase;
28import org.openstreetmap.josm.data.Bounds;
29import org.openstreetmap.josm.data.gpx.GpxConstants;
30import org.openstreetmap.josm.data.gpx.GpxData;
31import org.openstreetmap.josm.data.gpx.GpxTrack;
32import org.openstreetmap.josm.data.gpx.WayPoint;
33import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
34import org.openstreetmap.josm.data.projection.Projection;
35import org.openstreetmap.josm.gui.MapView;
36import org.openstreetmap.josm.gui.NavigatableComponent;
37import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
38import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
39import org.openstreetmap.josm.gui.layer.gpx.ChooseTrackVisibilityAction;
40import org.openstreetmap.josm.gui.layer.gpx.ConvertToDataLayerAction;
41import org.openstreetmap.josm.gui.layer.gpx.CustomizeDrawingAction;
42import org.openstreetmap.josm.gui.layer.gpx.DownloadAlongTrackAction;
43import org.openstreetmap.josm.gui.layer.gpx.DownloadWmsAlongTrackAction;
44import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
45import org.openstreetmap.josm.gui.layer.gpx.ImportAudioAction;
46import org.openstreetmap.josm.gui.layer.gpx.ImportImagesAction;
47import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction;
48import org.openstreetmap.josm.gui.widgets.HtmlPanel;
49import org.openstreetmap.josm.io.GpxImporter;
50import org.openstreetmap.josm.tools.ImageProvider;
51import org.openstreetmap.josm.tools.date.DateUtils;
52
53public class GpxLayer extends Layer {
54
55 public GpxData data;
56 private boolean isLocalFile;
57 // used by ChooseTrackVisibilityAction to determine which tracks to show/hide
58 public boolean[] trackVisibility = new boolean[0];
59
60 private final List<GpxTrack> lastTracks = new ArrayList<>(); // List of tracks at last paint
61 private int lastUpdateCount;
62
63 private final GpxDrawHelper drawHelper;
64
65 public GpxLayer(GpxData d) {
66 super(d.getString(GpxConstants.META_NAME));
67 data = d;
68 drawHelper = new GpxDrawHelper(data);
69 ensureTrackVisibilityLength();
70 }
71
72 public GpxLayer(GpxData d, String name) {
73 this(d);
74 this.setName(name);
75 }
76
77 public GpxLayer(GpxData d, String name, boolean isLocal) {
78 this(d);
79 this.setName(name);
80 this.isLocalFile = isLocal;
81 }
82
83 @Override
84 public Color getColor(boolean ignoreCustom) {
85 return drawHelper.getColor(getName(), ignoreCustom);
86 }
87
88 /**
89 * Returns a human readable string that shows the timespan of the given track
90 * @param trk The GPX track for which timespan is displayed
91 * @return The timespan as a string
92 */
93 public static String getTimespanForTrack(GpxTrack trk) {
94 Date[] bounds = GpxData.getMinMaxTimeForTrack(trk);
95 String ts = "";
96 if (bounds != null) {
97 DateFormat df = DateUtils.getDateFormat(DateFormat.SHORT);
98 String earliestDate = df.format(bounds[0]);
99 String latestDate = df.format(bounds[1]);
100
101 if (earliestDate.equals(latestDate)) {
102 DateFormat tf = DateUtils.getTimeFormat(DateFormat.SHORT);
103 ts += earliestDate + " ";
104 ts += tf.format(bounds[0]) + " - " + tf.format(bounds[1]);
105 } else {
106 DateFormat dtf = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM);
107 ts += dtf.format(bounds[0]) + " - " + dtf.format(bounds[1]);
108 }
109
110 int diff = (int) (bounds[1].getTime() - bounds[0].getTime()) / 1000;
111 ts += String.format(" (%d:%02d)", diff / 3600, (diff % 3600) / 60);
112 }
113 return ts;
114 }
115
116 @Override
117 public Icon getIcon() {
118 return ImageProvider.get("layer", "gpx_small");
119 }
120
121 @Override
122 public Object getInfoComponent() {
123 StringBuilder info = new StringBuilder();
124
125 if (data.attr.containsKey("name")) {
126 info.append(tr("Name: {0}", data.get(GpxConstants.META_NAME))).append("<br>");
127 }
128
129 if (data.attr.containsKey("desc")) {
130 info.append(tr("Description: {0}", data.get(GpxConstants.META_DESC))).append("<br>");
131 }
132
133 if (!data.tracks.isEmpty()) {
134 info.append("<table><thead align='center'><tr><td colspan='5'>"
135 + trn("{0} track", "{0} tracks", data.tracks.size(), data.tracks.size())
136 + "</td></tr><tr align='center'><td>" + tr("Name") + "</td><td>"
137 + tr("Description") + "</td><td>" + tr("Timespan")
138 + "</td><td>" + tr("Length") + "</td><td>" + tr("URL")
139 + "</td></tr></thead>");
140
141 for (GpxTrack trk : data.tracks) {
142 info.append("<tr><td>");
143 if (trk.getAttributes().containsKey(GpxConstants.GPX_NAME)) {
144 info.append(trk.get(GpxConstants.GPX_NAME));
145 }
146 info.append("</td><td>");
147 if (trk.getAttributes().containsKey(GpxConstants.GPX_DESC)) {
148 info.append(" ").append(trk.get(GpxConstants.GPX_DESC));
149 }
150 info.append("</td><td>");
151 info.append(getTimespanForTrack(trk));
152 info.append("</td><td>");
153 info.append(NavigatableComponent.getSystemOfMeasurement().getDistText(trk.length()));
154 info.append("</td><td>");
155 if (trk.getAttributes().containsKey("url")) {
156 info.append(trk.get("url"));
157 }
158 info.append("</td></tr>");
159 }
160
161 info.append("</table><br><br>");
162
163 }
164
165 info.append(tr("Length: {0}", NavigatableComponent.getSystemOfMeasurement().getDistText(data.length()))).append("<br>");
166
167 info.append(trn("{0} route, ", "{0} routes, ", data.routes.size(), data.routes.size())).append(
168 trn("{0} waypoint", "{0} waypoints", data.waypoints.size(), data.waypoints.size())).append("<br>");
169
170 final JScrollPane sp = new JScrollPane(new HtmlPanel(info.toString()));
171 sp.setPreferredSize(new Dimension(sp.getPreferredSize().width+20, 370));
172 SwingUtilities.invokeLater(new Runnable() {
173 @Override
174 public void run() {
175 sp.getVerticalScrollBar().setValue(0);
176 }
177 });
178 return sp;
179 }
180
181 @Override
182 public boolean isInfoResizable() {
183 return true;
184 }
185
186 @Override
187 public Action[] getMenuEntries() {
188 return new Action[] {
189 LayerListDialog.getInstance().createShowHideLayerAction(),
190 LayerListDialog.getInstance().createDeleteLayerAction(),
191 SeparatorLayerAction.INSTANCE,
192 new LayerSaveAction(this),
193 new LayerSaveAsAction(this),
194 new CustomizeColor(this),
195 new CustomizeDrawingAction(this),
196 new ImportImagesAction(this),
197 new ImportAudioAction(this),
198 new MarkersFromNamedPointsAction(this),
199 new ConvertToDataLayerAction(this),
200 new DownloadAlongTrackAction(data),
201 new DownloadWmsAlongTrackAction(data),
202 SeparatorLayerAction.INSTANCE,
203 new ChooseTrackVisibilityAction(this),
204 new RenameLayerAction(getAssociatedFile(), this),
205 SeparatorLayerAction.INSTANCE,
206 new LayerListPopup.InfoAction(this) };
207 }
208
209 public boolean isLocalFile() {
210 return isLocalFile;
211 }
212
213 @Override
214 public String getToolTipText() {
215 StringBuilder info = new StringBuilder().append("<html>");
216
217 if (data.attr.containsKey(GpxConstants.META_NAME)) {
218 info.append(tr("Name: {0}", data.get(GpxConstants.META_NAME))).append("<br>");
219 }
220
221 if (data.attr.containsKey(GpxConstants.META_DESC)) {
222 info.append(tr("Description: {0}", data.get(GpxConstants.META_DESC))).append("<br>");
223 }
224
225 info.append(trn("{0} track, ", "{0} tracks, ", data.tracks.size(), data.tracks.size()));
226 info.append(trn("{0} route, ", "{0} routes, ", data.routes.size(), data.routes.size()));
227 info.append(trn("{0} waypoint", "{0} waypoints", data.waypoints.size(), data.waypoints.size())).append("<br>");
228
229 info.append(tr("Length: {0}", NavigatableComponent.getSystemOfMeasurement().getDistText(data.length())));
230 info.append("<br>");
231
232 return info.append("</html>").toString();
233 }
234
235 @Override
236 public boolean isMergable(Layer other) {
237 return other instanceof GpxLayer;
238 }
239
240 private int sumUpdateCount() {
241 int updateCount = 0;
242 for (GpxTrack track: data.tracks) {
243 updateCount += track.getUpdateCount();
244 }
245 return updateCount;
246 }
247
248 @Override
249 public boolean isChanged() {
250 if (data.tracks.equals(lastTracks))
251 return sumUpdateCount() != lastUpdateCount;
252 else
253 return true;
254 }
255
256 public void filterTracksByDate(Date fromDate, Date toDate, boolean showWithoutDate) {
257 int i = 0;
258 long from = fromDate.getTime();
259 long to = toDate.getTime();
260 for (GpxTrack trk : data.tracks) {
261 Date[] t = GpxData.getMinMaxTimeForTrack(trk);
262
263 if (t==null) continue;
264 long tm = t[1].getTime();
265 trackVisibility[i]= (tm==0 && showWithoutDate) || (from <= tm && tm <= to);
266 i++;
267 }
268 }
269
270 @Override
271 public void mergeFrom(Layer from) {
272 data.mergeFrom(((GpxLayer) from).data);
273 drawHelper.dataChanged();
274 }
275
276 @Override
277 public void paint(Graphics2D g, MapView mv, Bounds box) {
278 lastUpdateCount = sumUpdateCount();
279 lastTracks.clear();
280 lastTracks.addAll(data.tracks);
281
282 List<WayPoint> visibleSegments = listVisibleSegments(box);
283 if (!visibleSegments.isEmpty()) {
284 drawHelper.readPreferences(getName());
285 drawHelper.drawAll(g, mv, visibleSegments);
286 if (Main.map.mapView.getActiveLayer() == this) {
287 drawHelper.drawColorBar(g, mv);
288 }
289 }
290 }
291
292 private List<WayPoint> listVisibleSegments(Bounds box) {
293 WayPoint last = null;
294 LinkedList<WayPoint> visibleSegments = new LinkedList<>();
295
296 ensureTrackVisibilityLength();
297 for (Collection<WayPoint> segment : data.getLinesIterable(trackVisibility)) {
298
299 for (WayPoint pt : segment) {
300 Bounds b = new Bounds(pt.getCoor());
301 if (pt.drawLine && last != null) {
302 b.extend(last.getCoor());
303 }
304 if (b.intersects(box)) {
305 if (last != null && (visibleSegments.isEmpty()
306 || visibleSegments.getLast() != last)) {
307 if (last.drawLine) {
308 WayPoint l = new WayPoint(last);
309 l.drawLine = false;
310 visibleSegments.add(l);
311 } else {
312 visibleSegments.add(last);
313 }
314 }
315 visibleSegments.add(pt);
316 }
317 last = pt;
318 }
319 }
320 return visibleSegments;
321 }
322
323 @Override
324 public void visitBoundingBox(BoundingXYVisitor v) {
325 v.visit(data.recalculateBounds());
326 }
327
328 @Override
329 public File getAssociatedFile() {
330 return data.storageFile;
331 }
332
333 @Override
334 public void setAssociatedFile(File file) {
335 data.storageFile = file;
336 }
337
338 /** ensures the trackVisibility array has the correct length without losing data.
339 * additional entries are initialized to true;
340 */
341 private void ensureTrackVisibilityLength() {
342 final int l = data.tracks.size();
343 if (l == trackVisibility.length)
344 return;
345 final int m = Math.min(l, trackVisibility.length);
346 trackVisibility = Arrays.copyOf(trackVisibility, l);
347 for (int i = m; i < l; i++) {
348 trackVisibility[i] = true;
349 }
350 }
351
352 @Override
353 public void projectionChanged(Projection oldValue, Projection newValue) {
354 if (newValue == null) return;
355 data.resetEastNorthCache();
356 }
357
358 @Override
359 public boolean isSavable() {
360 return true; // With GpxExporter
361 }
362
363 @Override
364 public boolean checkSaveConditions() {
365 return data != null;
366 }
367
368 @Override
369 public File createAndOpenSaveFileChooser() {
370 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save GPX file"), GpxImporter.FILE_FILTER);
371 }
372
373}
Note: See TracBrowser for help on using the repository browser.