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

Last change on this file since 8792 was 8557, checked in by Don-vip, 9 years ago

checkstyle

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