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

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

see #11924 - write proper html code to see it it resolves unit test failing only with jdk9

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