source: josm/trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java@ 18287

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

fix #20913 - fix handling of GPX files in sessions (patch by Bjoeni)

  • revert r17659 (except for unit tests) - see #20233
    • don't save GPX track and marker colors in session file anymore, but in the GPX files as extensions (like before)
    • don't ask to save unmodified GPX file
    • don't override global color settings when individual track doesn't have a color
  • ask user to save changes to GPX file when any drawing settings or marker colors have changed (currently only happens for track colors)
  • save marker color values to session even when corresponding GPX layer has already been deleted
  • save alpha values for GPX marker colors
  • added explanation to the "overwrite GPX file" dialog
  • inform user if not all files referenced by the session file are saved yet
  • allow user to save all files that are not included in the *.jos/*.joz but are only referenced in the session file
  • display * next to GPX layers that need to be saved (move isDirty() logic from OsmDataLayer to AbstractModifiableLayer)
  • Property svn:eol-style set to native
File size: 18.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.gpx;
3
4import static org.junit.jupiter.api.Assertions.assertEquals;
5import static org.junit.jupiter.api.Assertions.assertFalse;
6import static org.junit.jupiter.api.Assertions.assertNotNull;
7import static org.junit.jupiter.api.Assertions.assertNull;
8import static org.junit.jupiter.api.Assertions.assertThrows;
9import static org.junit.jupiter.api.Assertions.assertTrue;
10
11import java.io.IOException;
12import java.time.Instant;
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.List;
18import java.util.stream.Collectors;
19import java.util.stream.Stream;
20
21import org.junit.jupiter.api.BeforeEach;
22import org.junit.jupiter.api.Test;
23import org.junit.jupiter.api.extension.RegisterExtension;
24import org.openstreetmap.josm.TestUtils;
25import org.openstreetmap.josm.data.Bounds;
26import org.openstreetmap.josm.data.DataSource;
27import org.openstreetmap.josm.data.coor.EastNorth;
28import org.openstreetmap.josm.data.coor.LatLon;
29import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeEvent;
30import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
31import org.openstreetmap.josm.data.projection.ProjectionRegistry;
32import org.openstreetmap.josm.io.GpxReaderTest;
33import org.openstreetmap.josm.testutils.JOSMTestRules;
34import org.openstreetmap.josm.tools.ListenerList;
35import org.openstreetmap.josm.tools.date.Interval;
36import org.xml.sax.SAXException;
37
38import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
39import nl.jqno.equalsverifier.EqualsVerifier;
40import nl.jqno.equalsverifier.Warning;
41
42/**
43 * Unit tests for class {@link GpxData}.
44 */
45class GpxDataTest {
46
47 /**
48 * Setup test.
49 */
50 @RegisterExtension
51 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
52 public JOSMTestRules test = new JOSMTestRules().projection();
53
54 private GpxData data;
55
56 /**
57 * Set up empty test data
58 */
59 @BeforeEach
60 public void setUp() {
61 data = new GpxData();
62 }
63
64 /**
65 * Test method for {@link GpxData#mergeFrom(GpxData)}.
66 */
67 @Test
68 void testMergeFrom() {
69 GpxTrack track = singleWaypointGpxTrack();
70 GpxRoute route = singleWaypointRoute();
71 WayPoint newWP = new WayPoint(LatLon.NORTH_POLE);
72 WayPoint existingWP = new WayPoint(LatLon.SOUTH_POLE);
73
74 GpxData dataToMerge = new GpxData();
75 dataToMerge.addTrack(track);
76 dataToMerge.addRoute(route);
77 dataToMerge.addWaypoint(newWP);
78
79 data.addWaypoint(existingWP);
80 data.mergeFrom(dataToMerge);
81
82 assertEquals(1, data.getTracks().size());
83 assertEquals(1, data.getRoutes().size());
84 assertEquals(2, data.getWaypoints().size());
85
86 assertTrue(data.getTracks().contains(track));
87 assertTrue(data.getRoutes().contains(route));
88 assertTrue(data.getWaypoints().contains(newWP));
89 assertTrue(data.getWaypoints().contains(existingWP));
90 }
91
92 /**
93 * Test method for {@link GpxData#mergeFrom(GpxData, boolean, boolean)} including cutting/connecting tracks using actual files.
94 * @throws Exception if the track cannot be parsed
95 */
96 @Test
97 void testMergeFromFiles() throws Exception {
98 testMerge(false, false, "Merged-all"); // regular merging
99 testMerge(true, false, "Merged-cut"); // cut overlapping tracks, but do not connect them
100 testMerge(true, true, "Merged-cut-connect"); // cut overlapping tracks and connect them
101 }
102
103 private static void testMerge(boolean cut, boolean connect, String exp) throws IOException, SAXException {
104 final GpxData own = getGpx("Layer1");
105 final GpxData other = getGpx("Layer2");
106 final GpxData expected = getGpx(exp);
107 own.mergeFrom(other, cut, connect);
108 own.put(GpxConstants.META_BOUNDS, null);
109 expected.put(GpxConstants.META_BOUNDS, null); //they are only updated by GpxWriter
110 assertEquals(expected, own, exp + " didn't match!");
111 }
112
113 private static GpxData getGpx(String file) throws IOException, SAXException {
114 return GpxReaderTest.parseGpxData(TestUtils.getTestDataRoot() + "mergelayers/" + file + ".gpx");
115 }
116
117 /**
118 * Test method for {@link GpxData#getTracks()}, {@link GpxData#addTrack(IGpxTrack)}, {@link GpxData#removeTrack(IGpxTrack)}.
119 */
120 @Test
121 void testTracks() {
122 assertEquals(0, data.getTracks().size());
123
124 GpxTrack track1 = emptyGpxTrack();
125 GpxTrack track2 = singleWaypointGpxTrack();
126 data.addTrack(track1);
127 assertEquals(1, data.getTracks().size());
128 data.addTrack(track2);
129 assertEquals(2, data.getTracks().size());
130 assertTrue(data.getTracks().contains(track1));
131 assertTrue(data.getTracks().contains(track2));
132
133 data.removeTrack(track1);
134 assertEquals(1, data.getTracks().size());
135 assertFalse(data.getTracks().contains(track1));
136 assertTrue(data.getTracks().contains(track2));
137 }
138
139 /**
140 * Test method for {@link GpxData#addTrack(IGpxTrack)}.
141 */
142 @Test
143 void testAddTrackFails() {
144 GpxTrack track1 = emptyGpxTrack();
145 data.addTrack(track1);
146 assertThrows(IllegalArgumentException.class, () -> data.addTrack(track1));
147 }
148
149 /**
150 * Test method for {@link GpxData#removeTrack(IGpxTrack)}.
151 */
152 @Test
153 void testRemoveTrackFails() {
154 GpxTrack track1 = emptyGpxTrack();
155 data.addTrack(track1);
156 data.removeTrack(track1);
157 assertThrows(IllegalArgumentException.class, () -> data.removeTrack(track1));
158 }
159
160 /**
161 * Test method for {@link GpxData#getRoutes()}, {@link GpxData#addRoute(GpxRoute)}, {@link GpxData#removeRoute(GpxRoute)}.
162 */
163 @Test
164 void testRoutes() {
165 assertEquals(0, data.getTracks().size());
166
167 GpxRoute route1 = new GpxRoute();
168 GpxRoute route2 = new GpxRoute();
169 route2.routePoints.add(new WayPoint(LatLon.NORTH_POLE));
170 data.addRoute(route1);
171 assertEquals(1, data.getRoutes().size());
172 data.addRoute(route2);
173 assertEquals(2, data.getRoutes().size());
174 assertTrue(data.getRoutes().contains(route1));
175 assertTrue(data.getRoutes().contains(route2));
176
177 data.removeRoute(route1);
178 assertEquals(1, data.getRoutes().size());
179 assertFalse(data.getRoutes().contains(route1));
180 assertTrue(data.getRoutes().contains(route2));
181 }
182
183 /**
184 * Test method for {@link GpxData#addRoute(GpxRoute)}.
185 */
186 @Test
187 void testAddRouteFails() {
188 GpxRoute route1 = new GpxRoute();
189 data.addRoute(route1);
190 assertThrows(IllegalArgumentException.class, () -> data.addRoute(route1));
191 }
192
193 /**
194 * Test method for {@link GpxData#removeRoute(GpxRoute)}.
195 */
196 @Test
197 void testRemoveRouteFails() {
198 GpxRoute route1 = new GpxRoute();
199 data.addRoute(route1);
200 data.removeRoute(route1);
201 assertThrows(IllegalArgumentException.class, () -> data.removeRoute(route1));
202 }
203
204 /**
205 * Test method for {@link GpxData#getWaypoints()}, {@link GpxData#addWaypoint(WayPoint)}, {@link GpxData#removeWaypoint(WayPoint)}.
206 */
207 @Test
208 void testWaypoints() {
209 assertEquals(0, data.getTracks().size());
210
211 WayPoint waypoint1 = new WayPoint(LatLon.ZERO);
212 WayPoint waypoint2 = new WayPoint(LatLon.NORTH_POLE);
213 data.addWaypoint(waypoint1);
214 assertEquals(1, data.getWaypoints().size());
215 data.addWaypoint(waypoint2);
216 assertEquals(2, data.getWaypoints().size());
217 assertTrue(data.getWaypoints().contains(waypoint1));
218 assertTrue(data.getWaypoints().contains(waypoint2));
219
220 data.removeWaypoint(waypoint1);
221 assertEquals(1, data.getWaypoints().size());
222 assertFalse(data.getWaypoints().contains(waypoint1));
223 assertTrue(data.getWaypoints().contains(waypoint2));
224 }
225
226 /**
227 * Test method for {@link GpxData#addWaypoint(WayPoint)}.
228 */
229 @Test
230 void testAddWaypointFails() {
231 WayPoint waypoint1 = new WayPoint(LatLon.ZERO);
232 data.addWaypoint(waypoint1);
233 assertThrows(IllegalArgumentException.class, () -> data.addWaypoint(waypoint1));
234 }
235
236 /**
237 * Test method for {@link GpxData#removeWaypoint(WayPoint)}.
238 */
239 @Test
240 void testRemoveWaypointFails() {
241 WayPoint waypoint1 = new WayPoint(LatLon.ZERO);
242 data.addWaypoint(waypoint1);
243 data.removeWaypoint(waypoint1);
244 assertThrows(IllegalArgumentException.class, () -> data.removeWaypoint(waypoint1));
245 }
246
247 /**
248 * Test method for {@link GpxData#hasTrackPoints()}.
249 */
250 @Test
251 void testHasTrackPoints() {
252 assertFalse(data.hasTrackPoints());
253 GpxTrack track1 = emptyGpxTrack();
254 data.addTrack(track1);
255 assertFalse(data.hasTrackPoints());
256 GpxTrack track2 = singleWaypointGpxTrack();
257 data.addTrack(track2);
258 assertTrue(data.hasTrackPoints());
259 }
260
261 /**
262 * Test method for {@link GpxData#getTrackPoints()}.
263 */
264 @Test
265 void testGetTrackPoints() {
266 assertEquals(0, data.getTrackPoints().count());
267 GpxTrack track1 = singleWaypointGpxTrack();
268 data.addTrack(track1);
269 assertEquals(1, data.getTrackPoints().count());
270 GpxTrack track2 = singleWaypointGpxTrack();
271 data.addTrack(track2);
272 assertEquals(2, data.getTrackPoints().count());
273 }
274
275 /**
276 * Test method for {@link GpxData#hasRoutePoints()}.
277 */
278 @Test
279 void testHasRoutePoints() {
280
281 }
282
283 /**
284 * Test method for {@link GpxData#isEmpty()}.
285 */
286 @Test
287 void testIsEmpty() {
288 GpxTrack track1 = singleWaypointGpxTrack();
289 WayPoint waypoint = new WayPoint(LatLon.ZERO);
290 GpxRoute route = singleWaypointRoute();
291
292 assertTrue(data.isEmpty());
293
294 data.addTrack(track1);
295 assertFalse(data.isEmpty());
296 data.removeTrack(track1);
297 assertTrue(data.isEmpty());
298
299 data.addWaypoint(waypoint);
300 assertFalse(data.isEmpty());
301 data.removeWaypoint(waypoint);
302 assertTrue(data.isEmpty());
303
304 data.addRoute(route);
305 assertFalse(data.isEmpty());
306 data.removeRoute(route);
307 assertTrue(data.isEmpty());
308 }
309
310 /**
311 * Test method for {@link GpxData#length()}.
312 */
313 @Test
314 void testLength() {
315 GpxTrack track1 = waypointGpxTrack(
316 new WayPoint(new LatLon(0, 0)),
317 new WayPoint(new LatLon(1, 1)),
318 new WayPoint(new LatLon(0, 2)));
319 GpxTrack track2 = waypointGpxTrack(
320 new WayPoint(new LatLon(0, 0)),
321 new WayPoint(new LatLon(-1, 1)));
322 data.addTrack(track1);
323 data.addTrack(track2);
324 assertEquals(3 * new LatLon(0, 0).greatCircleDistance(new LatLon(1, 1)), data.length(), 1);
325 }
326
327 /**
328 * Test method for {@link GpxData#getOrderedTracks()}.
329 */
330 @Test
331 void testGetOrderedTracks() {
332 assertTrue(data.getOrderedTracks().isEmpty());
333
334 WayPoint p1 = new WayPoint(LatLon.NORTH_POLE);
335 WayPoint p2 = new WayPoint(LatLon.NORTH_POLE);
336
337 p1.setInstant(Instant.ofEpochMilli(100020));
338 p2.setInstant(Instant.ofEpochMilli(200020));
339
340 data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p2)), Collections.emptyMap()));
341 data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p1)), Collections.emptyMap()));
342
343 List<IGpxTrack> tracks = data.getOrderedTracks();
344 assertEquals(2, tracks.size());
345
346 assertEquals(p1, tracks.get(0).getSegments().iterator().next().getWayPoints().iterator().next());
347 assertEquals(p2, tracks.get(1).getSegments().iterator().next().getWayPoints().iterator().next());
348 }
349
350 /**
351 * Test method for {@link GpxData#getMinMaxTimeForAllTracks()}.
352 */
353 @Test
354 void testGetMinMaxTimeForAllTracks() {
355 assertFalse(data.getMinMaxTimeForAllTracks().isPresent());
356
357 WayPoint p1 = new WayPoint(LatLon.NORTH_POLE);
358 WayPoint p2 = new WayPoint(LatLon.NORTH_POLE);
359 WayPoint p3 = new WayPoint(LatLon.NORTH_POLE);
360 WayPoint p4 = new WayPoint(LatLon.NORTH_POLE);
361 WayPoint p5 = new WayPoint(LatLon.NORTH_POLE);
362 p1.setInstant(Instant.ofEpochMilli(200020));
363 p2.setInstant(Instant.ofEpochMilli(100020));
364 p4.setInstant(Instant.ofEpochMilli(500020));
365 data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p1, p2)), Collections.emptyMap()));
366 data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p3, p4, p5)), Collections.emptyMap()));
367
368 Interval times = data.getMinMaxTimeForAllTracks().orElse(null);
369 assertEquals("1970-01-01T00:01:40.020Z/1970-01-01T00:08:20.020Z", times.toString());
370 assertEquals(Instant.ofEpochMilli(100020), times.getStart());
371 assertEquals(Instant.ofEpochMilli(500020), times.getEnd());
372 }
373
374 /**
375 * Test method for {@link GpxData#nearestPointOnTrack(org.openstreetmap.josm.data.coor.EastNorth, double)}.
376 */
377 @Test
378 void testNearestPointOnTrack() {
379 List<WayPoint> points = Stream
380 .of(new EastNorth(10, 10), new EastNorth(10, 0), new EastNorth(-1, 0))
381 .map(ProjectionRegistry.getProjection()::eastNorth2latlon)
382 .map(WayPoint::new)
383 .collect(Collectors.toList());
384 data.addTrack(new GpxTrack(Arrays.asList(points), Collections.emptyMap()));
385
386 WayPoint closeToMiddle = data.nearestPointOnTrack(new EastNorth(10, 0), 10);
387 assertEquals(points.get(1), closeToMiddle);
388
389 WayPoint close = data.nearestPointOnTrack(new EastNorth(5, 5), 10);
390 assertEquals(10, close.getEastNorth(ProjectionRegistry.getProjection()).east(), .01);
391 assertEquals(5, close.getEastNorth(ProjectionRegistry.getProjection()).north(), .01);
392
393 close = data.nearestPointOnTrack(new EastNorth(15, 5), 10);
394 assertEquals(10, close.getEastNorth(ProjectionRegistry.getProjection()).east(), .01);
395 assertEquals(5, close.getEastNorth(ProjectionRegistry.getProjection()).north(), .01);
396
397 assertNull(data.nearestPointOnTrack(new EastNorth(5, 5), 1));
398 }
399
400 /**
401 * Test method for {@link GpxData#getDataSources()}.
402 */
403 @Test
404 void testGetDataSources() {
405 DataSource ds = new DataSource(new Bounds(0, 0, 1, 1), "test");
406 data.dataSources.add(ds);
407 assertEquals(new ArrayList<>(Arrays.asList(ds)), new ArrayList<>(data.getDataSources()));
408 }
409
410 /**
411 * Test method for {@link GpxData#getDataSourceArea()}.
412 */
413 @Test
414 void testGetDataSourceArea() {
415 DataSource ds = new DataSource(new Bounds(0, 0, 1, 1), "test");
416 data.dataSources.add(ds);
417 assertNotNull(data.getDataSourceArea());
418 assertTrue(data.getDataSourceArea().contains(0.5, 0.5));
419 assertFalse(data.getDataSourceArea().contains(0.5, 1.5));
420 }
421
422 /**
423 * Test method for {@link GpxData#getDataSourceBounds()}.
424 */
425 @Test
426 void testGetDataSourceBounds() {
427 Bounds bounds = new Bounds(0, 0, 1, 1);
428 DataSource ds = new DataSource(bounds, "test");
429 data.dataSources.add(ds);
430 assertEquals(Arrays.asList(bounds), data.getDataSourceBounds());
431 }
432
433 /**
434 * Test method for {@link GpxData#addChangeListener(GpxData.GpxDataChangeListener)},
435 * {@link GpxData#addWeakChangeListener(GpxData.GpxDataChangeListener)},
436 * {@link GpxData#removeChangeListener(GpxData.GpxDataChangeListener)}.
437 */
438 @Test
439 void testChangeListener() {
440 TestChangeListener cl1 = new TestChangeListener();
441 TestChangeListener cl2 = new TestChangeListener();
442
443 data.addChangeListener(cl1);
444 data.addWeakChangeListener(cl2);
445 assertNull(cl1.lastEvent);
446 assertNull(cl2.lastEvent);
447
448 data.addTrack(singleWaypointGpxTrack());
449 assertEquals(data, cl1.lastEvent.getSource());
450 assertEquals(data, cl2.lastEvent.getSource());
451 cl1.lastEvent = null;
452 cl2.lastEvent = null;
453
454 data.addRoute(singleWaypointRoute());
455 assertEquals(data, cl1.lastEvent.getSource());
456 assertEquals(data, cl2.lastEvent.getSource());
457 cl1.lastEvent = null;
458 cl2.lastEvent = null;
459
460 data.removeChangeListener(cl1);
461 data.removeChangeListener(cl2);
462 data.addTrack(singleWaypointGpxTrack());
463 assertNull(cl1.lastEvent);
464 assertNull(cl2.lastEvent);
465 }
466
467 private static class TestChangeListener implements GpxDataChangeListener {
468
469 private GpxDataChangeEvent lastEvent;
470
471 @Override
472 public void gpxDataChanged(GpxDataChangeEvent e) {
473 lastEvent = e;
474 }
475
476 }
477
478 private static GpxTrack emptyGpxTrack() {
479 return new GpxTrack(Collections.<Collection<WayPoint>>emptyList(), Collections.emptyMap());
480 }
481
482 private static GpxTrack singleWaypointGpxTrack() {
483 return new GpxTrack(Collections.singleton(Collections.singleton(new WayPoint(LatLon.ZERO))), Collections.emptyMap());
484 }
485
486 private static GpxTrack waypointGpxTrack(WayPoint... wps) {
487 return new GpxTrack(Collections.singleton(Arrays.asList(wps)), Collections.emptyMap());
488 }
489
490 private static GpxRoute singleWaypointRoute() {
491 GpxRoute route = new GpxRoute();
492 route.routePoints.add(new WayPoint(LatLon.ZERO));
493 return route;
494 }
495
496 /**
497 * Unit test of methods {@link GpxData#equals} and {@link GpxData#hashCode}.
498 */
499 @Test
500 void testEqualsContract() {
501 TestUtils.assumeWorkingEqualsVerifier();
502 GpxExtensionCollection col = new GpxExtensionCollection();
503 col.add("josm", "from-server", "true");
504 EqualsVerifier.forClass(GpxData.class).usingGetClass()
505 .suppress(Warning.NONFINAL_FIELDS)
506 .withIgnoredFields("creator", "fromServer", "fromSession", "storageFile", "initializing", "updating",
507 "suppressedInvalidate", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans", "modified")
508 .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
509 .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create())
510 .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
511 .verify();
512 }
513}
Note: See TracBrowser for help on using the repository browser.