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

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

fix #10283 - wrong time format in GPX/images layer leading to wrong correlation (regression from r7299)

  • Property svn:eol-style set to native
File size: 13.2 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((String) d.attr.get("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.attr.get(GpxConstants.META_NAME))).append("<br>");
127 }
128
129 if (data.attr.containsKey("desc")) {
130 info.append(tr("Description: {0}", data.attr.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("name")) {
144 info.append(trk.getAttributes().get("name"));
145 }
146 info.append("</td><td>");
147 if (trk.getAttributes().containsKey("desc")) {
148 info.append(" ").append(trk.getAttributes().get("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.getAttributes().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("name")) {
218 info.append(tr("Name: {0}", data.attr.get(GpxConstants.META_NAME))).append("<br>");
219 }
220
221 if (data.attr.containsKey("desc")) {
222 info.append(tr("Description: {0}", data.attr.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 LinkedList<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
293 private LinkedList<WayPoint> listVisibleSegments(Bounds box) {
294 WayPoint last = null;
295 LinkedList<WayPoint> visibleSegments = new LinkedList<>();
296
297 ensureTrackVisibilityLength();
298 for (Collection<WayPoint> segment : data.getLinesIterable(trackVisibility)) {
299
300 for(WayPoint pt : segment)
301 {
302 Bounds b = new Bounds(pt.getCoor());
303 // last should never be null when this is true!
304 if(pt.drawLine) {
305 b.extend(last.getCoor());
306 }
307 if(b.intersects(box))
308 {
309 if(last != null && (visibleSegments.isEmpty()
310 || visibleSegments.getLast() != last)) {
311 if(last.drawLine) {
312 WayPoint l = new WayPoint(last);
313 l.drawLine = false;
314 visibleSegments.add(l);
315 } else {
316 visibleSegments.add(last);
317 }
318 }
319 visibleSegments.add(pt);
320 }
321 last = pt;
322 }
323 }
324 return visibleSegments;
325 }
326
327 @Override
328 public void visitBoundingBox(BoundingXYVisitor v) {
329 v.visit(data.recalculateBounds());
330 }
331
332 @Override
333 public File getAssociatedFile() {
334 return data.storageFile;
335 }
336
337 @Override
338 public void setAssociatedFile(File file) {
339 data.storageFile = file;
340 }
341
342 /** ensures the trackVisibility array has the correct length without losing data.
343 * additional entries are initialized to true;
344 */
345 private void ensureTrackVisibilityLength() {
346 final int l = data.tracks.size();
347 if (l == trackVisibility.length)
348 return;
349 final int m = Math.min(l, trackVisibility.length);
350 trackVisibility = Arrays.copyOf(trackVisibility, l);
351 for (int i = m; i < l; i++) {
352 trackVisibility[i] = true;
353 }
354 }
355
356 @Override
357 public void projectionChanged(Projection oldValue, Projection newValue) {
358 if (newValue == null) return;
359 data.resetEastNorthCache();
360 }
361
362 @Override
363 public boolean isSavable() {
364 return true; // With GpxExporter
365 }
366
367 @Override
368 public boolean checkSaveConditions() {
369 return data != null;
370 }
371
372 @Override
373 public File createAndOpenSaveFileChooser() {
374 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save GPX file"), GpxImporter.FILE_FILTER);
375 }
376
377}
Note: See TracBrowser for help on using the repository browser.