source: josm/trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java @ 14159

Last change on this file since 14159 was 14159, checked in by Don-vip, 6 months ago

fix #16633 - add robustness against invalid time entries in GPX files

  • Property svn:eol-style set to native
File size: 6.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.gpx;
3
4import java.awt.Color;
5import java.time.DateTimeException;
6import java.util.ArrayList;
7import java.util.Date;
8import java.util.List;
9import java.util.Objects;
10
11import org.openstreetmap.josm.data.coor.EastNorth;
12import org.openstreetmap.josm.data.coor.ILatLon;
13import org.openstreetmap.josm.data.coor.LatLon;
14import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match;
15import org.openstreetmap.josm.data.projection.Projecting;
16import org.openstreetmap.josm.tools.Logging;
17import org.openstreetmap.josm.tools.UncheckedParseException;
18import org.openstreetmap.josm.tools.date.DateUtils;
19import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider;
20
21/**
22 * A point in the GPX data
23 * @since 12167 implements ILatLon
24 */
25public class WayPoint extends WithAttributes implements Comparable<WayPoint>, TemplateEngineDataProvider, ILatLon {
26
27    /**
28     * The seconds (not milliseconds!) since 1970-01-01 00:00 UTC
29     */
30    public double time;
31    /**
32     * The color to draw the segment before this point in
33     * @see #drawLine
34     */
35    public Color customColoring;
36    /**
37     * <code>true</code> indicates that the line before this point should be drawn
38     */
39    public boolean drawLine;
40    /**
41     * The direction of the line before this point. Used as cache to speed up drawing. Should not be relied on.
42     */
43    public int dir;
44
45    /**
46     * Constructs a new {@code WayPoint} from an existing one.
47     * @param p existing waypoint
48     */
49    public WayPoint(WayPoint p) {
50        attr.putAll(p.attr);
51        lat = p.lat;
52        lon = p.lon;
53        east = p.east;
54        north = p.north;
55        eastNorthCacheKey = p.eastNorthCacheKey;
56        time = p.time;
57        customColoring = p.customColoring;
58        drawLine = p.drawLine;
59        dir = p.dir;
60    }
61
62    /**
63     * Constructs a new {@code WayPoint} from lat/lon coordinates.
64     * @param ll lat/lon coordinates
65     */
66    public WayPoint(LatLon ll) {
67        lat = ll.lat();
68        lon = ll.lon();
69    }
70
71    /*
72     * We "inline" lat/lon, rather than usinga LatLon internally => reduces memory overhead. Relevant
73     * because a lot of GPX waypoints are created when GPS tracks are downloaded from the OSM server.
74     */
75    private final double lat;
76    private final double lon;
77
78    /*
79     * internal cache of projected coordinates
80     */
81    private double east = Double.NaN;
82    private double north = Double.NaN;
83    private Object eastNorthCacheKey;
84
85    /**
86     * Invalidate the internal cache of east/north coordinates.
87     */
88    public void invalidateEastNorthCache() {
89        this.east = Double.NaN;
90        this.north = Double.NaN;
91    }
92
93    /**
94     * Returns the waypoint coordinates.
95     * @return the waypoint coordinates
96     */
97    public final LatLon getCoor() {
98        return new LatLon(lat, lon);
99    }
100
101    @Override
102    public double lon() {
103        return lon;
104    }
105
106    @Override
107    public double lat() {
108        return lat;
109    }
110
111    @Override
112    public final EastNorth getEastNorth(Projecting projecting) {
113        Object newCacheKey = projecting.getCacheKey();
114        if (Double.isNaN(east) || Double.isNaN(north) || !Objects.equals(newCacheKey, this.eastNorthCacheKey)) {
115            // projected coordinates haven't been calculated yet,
116            // so fill the cache of the projected waypoint coordinates
117            EastNorth en = projecting.latlon2eastNorth(this);
118            this.east = en.east();
119            this.north = en.north();
120            this.eastNorthCacheKey = newCacheKey;
121        }
122        return new EastNorth(east, north);
123    }
124
125    @Override
126    public String toString() {
127        return "WayPoint (" + (attr.containsKey(GPX_NAME) ? get(GPX_NAME) + ", " : "") + getCoor() + ", " + attr + ')';
128    }
129
130    /**
131     * Sets the {@link #time} field as well as the {@link #PT_TIME} attribute to the specified time
132     *
133     * @param time the time to set
134     * @since 9383
135     */
136    public void setTime(Date time) {
137        this.time = time.getTime() / 1000.;
138        this.attr.put(PT_TIME, DateUtils.fromDate(time));
139    }
140
141    /**
142     * Convert the time stamp of the waypoint into seconds from the epoch
143     */
144    public void setTime() {
145        setTimeFromAttribute();
146    }
147
148    /**
149     * Set the the time stamp of the waypoint into seconds from the epoch,
150     * @param time millisecond from the epoch
151     * @since 13210
152     */
153    public void setTime(long time) {
154        this.time = time / 1000.;
155    }
156
157    /**
158     * Convert the time stamp of the waypoint into seconds from the epoch
159     * @return The parsed time if successful, or {@code null}
160     * @since 9383
161     */
162    public Date setTimeFromAttribute() {
163        if (attr.containsKey(PT_TIME)) {
164            try {
165                final Object obj = get(PT_TIME);
166                final Date date = obj instanceof Date ? (Date) obj : DateUtils.fromString(obj.toString());
167                time = date.getTime() / 1000.;
168                return date;
169            } catch (UncheckedParseException | DateTimeException e) {
170                Logging.warn(e);
171                time = 0;
172            }
173        }
174        return null;
175    }
176
177    @Override
178    public int compareTo(WayPoint w) {
179        return Double.compare(time, w.time);
180    }
181
182    /**
183     * Returns the waypoint time.
184     * @return the waypoint time
185     */
186    public Date getTime() {
187        return new Date((long) (time * 1000));
188    }
189
190    @Override
191    public Object getTemplateValue(String name, boolean special) {
192        if (!special)
193            return get(name);
194        else
195            return null;
196    }
197
198    @Override
199    public boolean evaluateCondition(Match condition) {
200        throw new UnsupportedOperationException();
201    }
202
203    @Override
204    public List<String> getTemplateKeys() {
205        return new ArrayList<>(attr.keySet());
206    }
207
208    @Override
209    public int hashCode() {
210        final int prime = 31;
211        int result = super.hashCode();
212        long temp = Double.doubleToLongBits(lat);
213        result = prime * result + (int) (temp ^ (temp >>> 32));
214        temp = Double.doubleToLongBits(lon);
215        result = prime * result + (int) (temp ^ (temp >>> 32));
216        temp = Double.doubleToLongBits(time);
217        result = prime * result + (int) (temp ^ (temp >>> 32));
218        return result;
219    }
220
221    @Override
222    public boolean equals(Object obj) {
223        if (this == obj)
224            return true;
225        if (obj == null || !super.equals(obj) || getClass() != obj.getClass())
226            return false;
227        WayPoint other = (WayPoint) obj;
228        return Double.doubleToLongBits(lat) == Double.doubleToLongBits(other.lat)
229            && Double.doubleToLongBits(lon) == Double.doubleToLongBits(other.lon)
230            && Double.doubleToLongBits(time) == Double.doubleToLongBits(other.time);
231    }
232}
Note: See TracBrowser for help on using the repository browser.