Changeset 12156 in josm for trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
- Timestamp:
- 2017-05-15T14:14:40+02:00 (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
r11747 r12156 3 3 4 4 import java.io.File; 5 import java.text.MessageFormat; 6 import java.util.AbstractCollection; 7 import java.util.ArrayList; 5 8 import java.util.Collection; 6 9 import java.util.Collections; … … 9 12 import java.util.HashSet; 10 13 import java.util.Iterator; 11 import java.util.LinkedList;12 14 import java.util.Map; 13 15 import java.util.NoSuchElementException; 14 16 import java.util.Set; 17 import java.util.stream.Stream; 15 18 16 19 import org.openstreetmap.josm.Main; … … 19 22 import org.openstreetmap.josm.data.DataSource; 20 23 import org.openstreetmap.josm.data.coor.EastNorth; 24 import org.openstreetmap.josm.data.gpx.GpxTrack.GpxTrackChangeListener; 25 import org.openstreetmap.josm.tools.ListenerList; 21 26 22 27 /** … … 35 40 public String creator; 36 41 37 /** Tracks */ 38 public final Collection<GpxTrack> tracks = new LinkedList<>(); 39 /** Routes */ 40 public final Collection<GpxRoute> routes = new LinkedList<>(); 41 /** Waypoints */ 42 public final Collection<WayPoint> waypoints = new LinkedList<>(); 42 private ArrayList<GpxTrack> privateTracks = new ArrayList<>(); 43 private ArrayList<GpxRoute> privateRoutes = new ArrayList<>(); 44 private ArrayList<WayPoint> privateWaypoints = new ArrayList<>(); 45 private final GpxTrackChangeListener proxy = e -> fireInvalidate(); 46 47 /** 48 * Tracks. Access is discouraged, use {@link #getTracks()} to read. 49 * @see #getTracks() 50 */ 51 public final Collection<GpxTrack> tracks = new ListeningCollection<GpxTrack>(privateTracks, this::fireInvalidate) { 52 53 @Override 54 protected void removed(GpxTrack cursor) { 55 cursor.removeListener(proxy); 56 super.removed(cursor); 57 } 58 59 @Override 60 protected void added(GpxTrack cursor) { 61 super.added(cursor); 62 cursor.addListener(proxy); 63 } 64 }; 65 66 /** 67 * Routes. Access is discouraged, use {@link #getTracks()} to read. 68 * @see #getRoutes() 69 */ 70 public final Collection<GpxRoute> routes = new ListeningCollection<>(privateRoutes, this::fireInvalidate); 71 72 /** 73 * Waypoints. Access is discouraged, use {@link #getTracks()} to read. 74 * @see #getWaypoints() 75 */ 76 public final Collection<WayPoint> waypoints = new ListeningCollection<>(privateWaypoints, this::fireInvalidate); 43 77 44 78 /** … … 49 83 */ 50 84 public final Set<DataSource> dataSources = new HashSet<>(); 85 86 private final ListenerList<GpxDataChangeListener> listeners = ListenerList.create(); 51 87 52 88 /** … … 72 108 } 73 109 } 74 tracks.addAll(other.tracks);75 routes.addAll(other.routes);76 waypoints.addAll(other.waypoints);110 privateTracks.addAll(other.getTracks()); 111 privateRoutes.addAll(other.getRoutes()); 112 privateWaypoints.addAll(other.getWaypoints()); 77 113 dataSources.addAll(other.dataSources); 114 fireInvalidate(); 115 } 116 117 /** 118 * Get all tracks contained in this data set. 119 * @return The tracks. 120 */ 121 public Collection<GpxTrack> getTracks() { 122 return Collections.unmodifiableCollection(privateTracks); 123 } 124 125 /** 126 * Add a new track 127 * @param track The new track 128 * @since 12156 129 */ 130 public void addTrack(GpxTrack track) { 131 if (privateTracks.contains(track)) { 132 throw new IllegalArgumentException(MessageFormat.format("The track was already added to this data: {0}", track)); 133 } 134 privateTracks.add(track); 135 track.addListener(proxy); 136 fireInvalidate(); 137 } 138 139 /** 140 * Remove a track 141 * @param track The old track 142 * @since 12156 143 */ 144 public void removeTrack(GpxTrack track) { 145 if (!privateTracks.remove(track)) { 146 throw new IllegalArgumentException(MessageFormat.format("The track was not in this data: {0}", track)); 147 } 148 track.removeListener(proxy); 149 fireInvalidate(); 150 } 151 152 /** 153 * Gets the list of all routes defined in this data set. 154 * @return The routes 155 * @since 12156 156 */ 157 public Collection<GpxRoute> getRoutes() { 158 return Collections.unmodifiableCollection(privateRoutes); 159 } 160 161 /** 162 * Add a new route 163 * @param route The new route 164 * @since 12156 165 */ 166 public void addRoute(GpxRoute route) { 167 if (privateRoutes.contains(route)) { 168 throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", route)); 169 } 170 privateRoutes.add(route); 171 fireInvalidate(); 172 } 173 174 /** 175 * Remove a route 176 * @param route The old route 177 * @since 12156 178 */ 179 public void removeRoute(GpxRoute route) { 180 if (!privateRoutes.remove(route)) { 181 throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", route)); 182 } 183 fireInvalidate(); 184 } 185 186 /** 187 * Gets a list of all way points in this data set. 188 * @return The way points. 189 * @since 12156 190 */ 191 public Collection<WayPoint> getWaypoints() { 192 return Collections.unmodifiableCollection(privateWaypoints); 193 } 194 195 /** 196 * Add a new waypoint 197 * @param waypoint The new waypoint 198 * @since 12156 199 */ 200 public void addWaypoint(WayPoint waypoint) { 201 if (privateWaypoints.contains(waypoint)) { 202 throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", waypoint)); 203 } 204 privateWaypoints.add(waypoint); 205 fireInvalidate(); 206 } 207 208 /** 209 * Remove a waypoint 210 * @param waypoint The old waypoint 211 * @since 12156 212 */ 213 public void removeWaypoint(WayPoint waypoint) { 214 if (!privateWaypoints.remove(waypoint)) { 215 throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", waypoint)); 216 } 217 fireInvalidate(); 78 218 } 79 219 … … 83 223 */ 84 224 public boolean hasTrackPoints() { 85 for (GpxTrack trk : tracks) { 86 for (GpxTrackSegment trkseg : trk.getSegments()) { 87 if (!trkseg.getWayPoints().isEmpty()) 88 return true; 89 } 90 } 91 return false; 225 return getTrackPoints().findAny().isPresent(); 226 } 227 228 /** 229 * Gets a stream of all track points in the segments of the tracks of this data. 230 * @return The stream 231 * @see #getTracks() 232 * @see GpxTrack#getSegments() 233 * @see GpxTrackSegment#getWayPoints() 234 * @since 12156 235 */ 236 public Stream<WayPoint> getTrackPoints() { 237 return getTracks().stream().flatMap(trk -> trk.getSegments().stream()).flatMap(trkseg -> trkseg.getWayPoints().stream()); 92 238 } 93 239 … … 97 243 */ 98 244 public boolean hasRoutePoints() { 99 for (GpxRoute rte : routes) { 100 if (!rte.routePoints.isEmpty()) 101 return true; 102 } 103 return false; 245 return getRoutes().stream().anyMatch(rte -> !rte.routePoints.isEmpty()); 104 246 } 105 247 … … 144 286 public Bounds recalculateBounds() { 145 287 Bounds bounds = null; 146 for (WayPoint wpt : waypoints) {288 for (WayPoint wpt : getWaypoints()) { 147 289 if (bounds == null) { 148 290 bounds = new Bounds(wpt.getCoor()); … … 151 293 } 152 294 } 153 for (GpxRoute rte : routes) {295 for (GpxRoute rte : getRoutes()) { 154 296 for (WayPoint wpt : rte.routePoints) { 155 297 if (bounds == null) { … … 160 302 } 161 303 } 162 for (GpxTrack trk : tracks) {304 for (GpxTrack trk : getTracks()) { 163 305 Bounds trkBounds = trk.getBounds(); 164 306 if (trkBounds != null) { … … 178 320 */ 179 321 public double length() { 180 double result = 0.0; // in meters 181 182 for (GpxTrack trk : tracks) { 183 result += trk.length(); 184 } 185 186 return result; 322 return getTracks().stream().mapToDouble(GpxTrack::length).sum(); 187 323 } 188 324 … … 261 397 double py = p.north(); 262 398 double rx = 0.0, ry = 0.0, sx, sy, x, y; 263 if (tracks == null) 264 return null; 265 for (GpxTrack track : tracks) { 399 for (GpxTrack track : getTracks()) { 266 400 for (GpxTrackSegment seg : track.getSegments()) { 267 401 WayPoint r = null; … … 352 486 */ 353 487 public void resetEastNorthCache() { 354 if (waypoints != null) { 355 for (WayPoint wp : waypoints) { 488 getWaypoints().forEach(WayPoint::invalidateEastNorthCache); 489 getTrackPoints().forEach(WayPoint::invalidateEastNorthCache); 490 for (GpxRoute route: getRoutes()) { 491 if (route.routePoints == null) { 492 continue; 493 } 494 for (WayPoint wp: route.routePoints) { 356 495 wp.invalidateEastNorthCache(); 357 }358 }359 if (tracks != null) {360 for (GpxTrack track: tracks) {361 for (GpxTrackSegment segment: track.getSegments()) {362 for (WayPoint wp: segment.getWayPoints()) {363 wp.invalidateEastNorthCache();364 }365 }366 }367 }368 if (routes != null) {369 for (GpxRoute route: routes) {370 if (route.routePoints == null) {371 continue;372 }373 for (WayPoint wp: route.routePoints) {374 wp.invalidateEastNorthCache();375 }376 496 } 377 497 } … … 498 618 return true; 499 619 } 620 621 /** 622 * Adds a listener that gets called whenever the data changed. 623 * @param listener The listener 624 * @since 12156 625 */ 626 public void addChangeListener(GpxDataChangeListener listener) { 627 listeners.addListener(listener); 628 } 629 630 /** 631 * Adds a listener that gets called whenever the data changed. It is added with a weak link 632 * @param listener The listener 633 */ 634 public void addWeakChangeListener(GpxDataChangeListener listener) { 635 listeners.addWeakListener(listener); 636 } 637 638 /** 639 * Removes a listener that gets called whenever the data changed. 640 * @param listener The listener 641 * @since 12156 642 */ 643 public void removeChangeListener(GpxDataChangeListener listener) { 644 listeners.removeListener(listener); 645 } 646 647 private void fireInvalidate() { 648 GpxDataChangeEvent e = new GpxDataChangeEvent(this); 649 listeners.fireEvent(l -> l.gpxDataChanged(e)); 650 } 651 652 /** 653 * This is a proxy of a collection that notifies a listener on every collection change 654 * @author Michael Zangl 655 * 656 * @param <T> The entry type 657 * @since 12156 658 */ 659 private static class ListeningCollection<T> extends AbstractCollection<T> { 660 private final ArrayList<T> base; 661 private final Runnable runOnModification; 662 663 ListeningCollection(ArrayList<T> base, Runnable runOnModification) { 664 this.base = base; 665 this.runOnModification = runOnModification; 666 } 667 668 @Override 669 public Iterator<T> iterator() { 670 Iterator<T> it = base.iterator(); 671 return new Iterator<T>() { 672 private T cursor; 673 674 @Override 675 public boolean hasNext() { 676 return it.hasNext(); 677 } 678 679 @Override 680 public T next() { 681 cursor = it.next(); 682 return cursor; 683 } 684 685 @Override 686 public void remove() { 687 if (cursor != null) { 688 removed(cursor); 689 cursor = null; 690 } 691 it.remove(); 692 } 693 }; 694 } 695 696 @Override 697 public int size() { 698 return base.size(); 699 } 700 701 @Override 702 public boolean remove(Object o) { 703 boolean remove = base.remove(o); 704 if (remove) { 705 removed((T) o); 706 } 707 return remove; 708 } 709 710 @Override 711 public boolean add(T e) { 712 boolean add = base.add(e); 713 added(e); 714 return add; 715 } 716 717 protected void removed(T cursor) { 718 runOnModification.run(); 719 } 720 721 protected void added(T cursor) { 722 runOnModification.run(); 723 } 724 } 725 726 /** 727 * A listener that listens to GPX data changes. 728 * @author Michael Zangl 729 * @since 12156 730 */ 731 @FunctionalInterface 732 public interface GpxDataChangeListener { 733 /** 734 * Called when the gpx data changed. 735 * @param e The event 736 */ 737 void gpxDataChanged(GpxDataChangeEvent e); 738 } 739 740 /** 741 * A data change event in any of the gpx data. 742 * @author Michael Zangl 743 * @since 12156 744 */ 745 public static class GpxDataChangeEvent { 746 private final GpxData source; 747 748 GpxDataChangeEvent(GpxData source) { 749 super(); 750 this.source = source; 751 } 752 753 /** 754 * Get the data that was changed. 755 * @return The data. 756 */ 757 public GpxData getSource() { 758 return source; 759 } 760 } 500 761 }
Note:
See TracChangeset
for help on using the changeset viewer.
