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

Last change on this file since 12987 was 12987, checked in by bastiK, 7 years ago

see #15410 - change preferences scheme for named colors - makes runtime color name registry obsolete

  • Property svn:eol-style set to native
File size: 12.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.Arrays;
12import java.util.Date;
13
14import javax.swing.Action;
15import javax.swing.Icon;
16import javax.swing.JScrollPane;
17import javax.swing.SwingUtilities;
18
19import org.openstreetmap.josm.actions.RenameLayerAction;
20import org.openstreetmap.josm.actions.SaveActionBase;
21import org.openstreetmap.josm.data.Bounds;
22import org.openstreetmap.josm.data.SystemOfMeasurement;
23import org.openstreetmap.josm.data.gpx.GpxConstants;
24import org.openstreetmap.josm.data.gpx.GpxData;
25import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
26import org.openstreetmap.josm.data.gpx.GpxTrack;
27import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
28import org.openstreetmap.josm.data.preferences.NamedColorProperty;
29import org.openstreetmap.josm.data.projection.Projection;
30import org.openstreetmap.josm.gui.MapView;
31import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
32import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
33import org.openstreetmap.josm.gui.io.importexport.GpxImporter;
34import org.openstreetmap.josm.gui.layer.gpx.ChooseTrackVisibilityAction;
35import org.openstreetmap.josm.gui.layer.gpx.ConvertToDataLayerAction;
36import org.openstreetmap.josm.gui.layer.gpx.CustomizeDrawingAction;
37import org.openstreetmap.josm.gui.layer.gpx.DownloadAlongTrackAction;
38import org.openstreetmap.josm.gui.layer.gpx.DownloadWmsAlongTrackAction;
39import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
40import org.openstreetmap.josm.gui.layer.gpx.ImportAudioAction;
41import org.openstreetmap.josm.gui.layer.gpx.ImportImagesAction;
42import org.openstreetmap.josm.gui.layer.gpx.MarkersFromNamedPointsAction;
43import org.openstreetmap.josm.gui.widgets.HtmlPanel;
44import org.openstreetmap.josm.tools.ImageProvider;
45import org.openstreetmap.josm.tools.date.DateUtils;
46
47/**
48 * A layer that displays data from a Gpx file / the OSM gpx downloads.
49 */
50public class GpxLayer extends Layer {
51
52 /** GPX data */
53 public GpxData data;
54 private final boolean isLocalFile;
55 /**
56 * used by {@link ChooseTrackVisibilityAction} to determine which tracks to show/hide
57 *
58 * Call {@link #invalidate()} after each change!
59 *
60 * TODO: Make it private, make it respond to track changes.
61 */
62 public boolean[] trackVisibility = new boolean[0];
63 /**
64 * Added as field to be kept as reference.
65 */
66 private final GpxDataChangeListener dataChangeListener = e -> this.invalidate();
67
68 /**
69 * Constructs a new {@code GpxLayer} without name.
70 * @param d GPX data
71 */
72 public GpxLayer(GpxData d) {
73 this(d, null, false);
74 }
75
76 /**
77 * Constructs a new {@code GpxLayer} with a given name.
78 * @param d GPX data
79 * @param name layer name
80 */
81 public GpxLayer(GpxData d, String name) {
82 this(d, name, false);
83 }
84
85 /**
86 * Constructs a new {@code GpxLayer} with a given name, thah can be attached to a local file.
87 * @param d GPX data
88 * @param name layer name
89 * @param isLocal whether data is attached to a local file
90 */
91 public GpxLayer(GpxData d, String name, boolean isLocal) {
92 super(d.getString(GpxConstants.META_NAME));
93 data = d;
94 data.addWeakChangeListener(dataChangeListener);
95 trackVisibility = new boolean[data.getTracks().size()];
96 Arrays.fill(trackVisibility, true);
97 setName(name);
98 isLocalFile = isLocal;
99 }
100
101 @Override
102 protected NamedColorProperty getBaseColorProperty() {
103 return GpxDrawHelper.DEFAULT_COLOR;
104 }
105
106 /**
107 * Returns a human readable string that shows the timespan of the given track
108 * @param trk The GPX track for which timespan is displayed
109 * @return The timespan as a string
110 */
111 public static String getTimespanForTrack(GpxTrack trk) {
112 Date[] bounds = GpxData.getMinMaxTimeForTrack(trk);
113 String ts = "";
114 if (bounds != null) {
115 DateFormat df = DateUtils.getDateFormat(DateFormat.SHORT);
116 String earliestDate = df.format(bounds[0]);
117 String latestDate = df.format(bounds[1]);
118
119 if (earliestDate.equals(latestDate)) {
120 DateFormat tf = DateUtils.getTimeFormat(DateFormat.SHORT);
121 ts += earliestDate + ' ';
122 ts += tf.format(bounds[0]) + " - " + tf.format(bounds[1]);
123 } else {
124 DateFormat dtf = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM);
125 ts += dtf.format(bounds[0]) + " - " + dtf.format(bounds[1]);
126 }
127
128 int diff = (int) (bounds[1].getTime() - bounds[0].getTime()) / 1000;
129 ts += String.format(" (%d:%02d)", diff / 3600, (diff % 3600) / 60);
130 }
131 return ts;
132 }
133
134 @Override
135 public Icon getIcon() {
136 return ImageProvider.get("layer", "gpx_small");
137 }
138
139 @Override
140 public Object getInfoComponent() {
141 StringBuilder info = new StringBuilder(48).append("<html>");
142
143 if (data.attr.containsKey("name")) {
144 info.append(tr("Name: {0}", data.get(GpxConstants.META_NAME))).append("<br>");
145 }
146
147 if (data.attr.containsKey("desc")) {
148 info.append(tr("Description: {0}", data.get(GpxConstants.META_DESC))).append("<br>");
149 }
150
151 if (!data.getTracks().isEmpty()) {
152 info.append("<table><thead align='center'><tr><td colspan='5'>")
153 .append(trn("{0} track", "{0} tracks", data.tracks.size(), data.tracks.size()))
154 .append("</td></tr><tr align='center'><td>").append(tr("Name")).append("</td><td>")
155 .append(tr("Description")).append("</td><td>").append(tr("Timespan"))
156 .append("</td><td>").append(tr("Length")).append("</td><td>").append(tr("URL"))
157 .append("</td></tr></thead>");
158
159 for (GpxTrack trk : data.getTracks()) {
160 info.append("<tr><td>");
161 if (trk.getAttributes().containsKey(GpxConstants.GPX_NAME)) {
162 info.append(trk.get(GpxConstants.GPX_NAME));
163 }
164 info.append("</td><td>");
165 if (trk.getAttributes().containsKey(GpxConstants.GPX_DESC)) {
166 info.append(' ').append(trk.get(GpxConstants.GPX_DESC));
167 }
168 info.append("</td><td>");
169 info.append(getTimespanForTrack(trk));
170 info.append("</td><td>");
171 info.append(SystemOfMeasurement.getSystemOfMeasurement().getDistText(trk.length()));
172 info.append("</td><td>");
173 if (trk.getAttributes().containsKey("url")) {
174 info.append(trk.get("url"));
175 }
176 info.append("</td></tr>");
177 }
178 info.append("</table><br><br>");
179 }
180
181 info.append(tr("Length: {0}", SystemOfMeasurement.getSystemOfMeasurement().getDistText(data.length()))).append("<br>")
182 .append(trn("{0} route, ", "{0} routes, ", data.getRoutes().size(), data.getRoutes().size()))
183 .append(trn("{0} waypoint", "{0} waypoints", data.getWaypoints().size(), data.getWaypoints().size())).append("<br></html>");
184
185 final JScrollPane sp = new JScrollPane(new HtmlPanel(info.toString()));
186 sp.setPreferredSize(new Dimension(sp.getPreferredSize().width+20, 370));
187 SwingUtilities.invokeLater(() -> sp.getVerticalScrollBar().setValue(0));
188 return sp;
189 }
190
191 @Override
192 public boolean isInfoResizable() {
193 return true;
194 }
195
196 @Override
197 public Action[] getMenuEntries() {
198 return new Action[] {
199 LayerListDialog.getInstance().createShowHideLayerAction(),
200 LayerListDialog.getInstance().createDeleteLayerAction(),
201 LayerListDialog.getInstance().createMergeLayerAction(this),
202 SeparatorLayerAction.INSTANCE,
203 new LayerSaveAction(this),
204 new LayerSaveAsAction(this),
205 new CustomizeColor(this),
206 new CustomizeDrawingAction(this),
207 new ImportImagesAction(this),
208 new ImportAudioAction(this),
209 new MarkersFromNamedPointsAction(this),
210 new ConvertToDataLayerAction.FromGpxLayer(this),
211 new DownloadAlongTrackAction(data),
212 new DownloadWmsAlongTrackAction(data),
213 SeparatorLayerAction.INSTANCE,
214 new ChooseTrackVisibilityAction(this),
215 new RenameLayerAction(getAssociatedFile(), this),
216 SeparatorLayerAction.INSTANCE,
217 new LayerListPopup.InfoAction(this) };
218 }
219
220 /**
221 * Determines if data is attached to a local file.
222 * @return {@code true} if data is attached to a local file, {@code false} otherwise
223 */
224 public boolean isLocalFile() {
225 return isLocalFile;
226 }
227
228 @Override
229 public String getToolTipText() {
230 StringBuilder info = new StringBuilder(48).append("<html>");
231
232 if (data.attr.containsKey(GpxConstants.META_NAME)) {
233 info.append(tr("Name: {0}", data.get(GpxConstants.META_NAME))).append("<br>");
234 }
235
236 if (data.attr.containsKey(GpxConstants.META_DESC)) {
237 info.append(tr("Description: {0}", data.get(GpxConstants.META_DESC))).append("<br>");
238 }
239
240 info.append(trn("{0} track, ", "{0} tracks, ", data.getTracks().size(), data.getTracks().size()))
241 .append(trn("{0} route, ", "{0} routes, ", data.getRoutes().size(), data.getRoutes().size()))
242 .append(trn("{0} waypoint", "{0} waypoints", data.getWaypoints().size(), data.getWaypoints().size())).append("<br>")
243 .append(tr("Length: {0}", SystemOfMeasurement.getSystemOfMeasurement().getDistText(data.length())))
244 .append("<br></html>");
245 return info.toString();
246 }
247
248 @Override
249 public boolean isMergable(Layer other) {
250 return other instanceof GpxLayer;
251 }
252
253 /**
254 * Shows/hides all tracks of a given date range by setting them to visible/invisible.
255 * @param fromDate The min date
256 * @param toDate The max date
257 * @param showWithoutDate Include tracks that don't have any date set..
258 */
259 public void filterTracksByDate(Date fromDate, Date toDate, boolean showWithoutDate) {
260 int i = 0;
261 long from = fromDate.getTime();
262 long to = toDate.getTime();
263 for (GpxTrack trk : data.getTracks()) {
264 Date[] t = GpxData.getMinMaxTimeForTrack(trk);
265
266 if (t == null) continue;
267 long tm = t[1].getTime();
268 trackVisibility[i] = (tm == 0 && showWithoutDate) || (from <= tm && tm <= to);
269 i++;
270 }
271 invalidate();
272 }
273
274 @Override
275 public void mergeFrom(Layer from) {
276 if (!(from instanceof GpxLayer))
277 throw new IllegalArgumentException("not a GpxLayer: " + from);
278 data.mergeFrom(((GpxLayer) from).data);
279 invalidate();
280 }
281
282 @Override
283 public void visitBoundingBox(BoundingXYVisitor v) {
284 v.visit(data.recalculateBounds());
285 }
286
287 @Override
288 public File getAssociatedFile() {
289 return data.storageFile;
290 }
291
292 @Override
293 public void setAssociatedFile(File file) {
294 data.storageFile = file;
295 }
296
297 @Override
298 public void projectionChanged(Projection oldValue, Projection newValue) {
299 if (newValue == null) return;
300 data.resetEastNorthCache();
301 }
302
303 @Override
304 public boolean isSavable() {
305 return true; // With GpxExporter
306 }
307
308 @Override
309 public boolean checkSaveConditions() {
310 return data != null;
311 }
312
313 @Override
314 public File createAndOpenSaveFileChooser() {
315 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save GPX file"), GpxImporter.getFileFilter());
316 }
317
318 @Override
319 public LayerPositionStrategy getDefaultLayerPosition() {
320 return LayerPositionStrategy.AFTER_LAST_DATA_LAYER;
321 }
322
323 @Override
324 public void paint(Graphics2D g, MapView mv, Bounds bbox) {
325 // unused - we use a painter so this is not called.
326 }
327
328 @Override
329 protected LayerPainter createMapViewPainter(MapViewEvent event) {
330 return new GpxDrawHelper(this);
331 }
332}
Note: See TracBrowser for help on using the repository browser.