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

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

findbugs - BC_UNCONFIRMED_CAST

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