001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.gpx;
003
004import java.awt.Color;
005import java.util.ArrayList;
006import java.util.Date;
007import java.util.List;
008
009import org.openstreetmap.josm.Main;
010import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
011import org.openstreetmap.josm.data.coor.EastNorth;
012import org.openstreetmap.josm.data.coor.LatLon;
013import org.openstreetmap.josm.data.projection.Projections;
014import org.openstreetmap.josm.tools.date.DateUtils;
015import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider;
016
017public class WayPoint extends WithAttributes implements Comparable<WayPoint>, TemplateEngineDataProvider {
018
019    /**
020     * The seconds (not milliseconds!) since 1970-01-01.
021     */
022    public double time;
023    public Color customColoring;
024    public boolean drawLine;
025    public int dir;
026
027    public WayPoint(WayPoint p) {
028        attr.putAll(p.attr);
029        lat = p.lat;
030        lon = p.lon;
031        east = p.east;
032        north = p.north;
033        time = p.time;
034        customColoring = p.customColoring;
035        drawLine = p.drawLine;
036        dir = p.dir;
037    }
038
039    public WayPoint(LatLon ll) {
040        lat = ll.lat();
041        lon = ll.lon();
042    }
043
044    /*
045     * We "inline" lat/lon, rather than usinga LatLon internally => reduces memory overhead. Relevant
046     * because a lot of GPX waypoints are created when GPS tracks are downloaded from the OSM server.
047     */
048    private final double lat;
049    private final double lon;
050
051    /*
052     * internal cache of projected coordinates
053     */
054    private double east = Double.NaN;
055    private double north = Double.NaN;
056
057    /**
058     * Invalidate the internal cache of east/north coordinates.
059     */
060    public void invalidateEastNorthCache() {
061        this.east = Double.NaN;
062        this.north = Double.NaN;
063    }
064
065    public final LatLon getCoor() {
066        return new LatLon(lat, lon);
067    }
068
069    /**
070     * <p>Replies the projected east/north coordinates.</p>
071     *
072     * <p>Uses the {@link Main#getProjection() global projection} to project the lan/lon-coordinates.
073     * Internally caches the projected coordinates.</p>
074     *
075     * <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must
076     * {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>
077     *
078     * @return the east north coordinates or {@code null}
079     * @see #invalidateEastNorthCache()
080     */
081    public final EastNorth getEastNorth() {
082        if (Double.isNaN(east) || Double.isNaN(north)) {
083            // projected coordinates haven't been calculated yet,
084            // so fill the cache of the projected waypoint coordinates
085            EastNorth en = Projections.project(new LatLon(lat, lon));
086            this.east = en.east();
087            this.north = en.north();
088        }
089        return new EastNorth(east, north);
090    }
091
092    @Override
093    public String toString() {
094        return "WayPoint (" + (attr.containsKey(GPX_NAME) ? get(GPX_NAME) + ", " : "") + getCoor() + ", " + attr + ')';
095    }
096
097    /**
098     * Sets the {@link #time} field as well as the {@link #PT_TIME} attribute to the specified time
099     *
100     * @param time the time to set
101     * @since 9383
102     */
103    public void setTime(Date time) {
104        this.time = time.getTime() / 1000.;
105        this.attr.put(PT_TIME, DateUtils.fromDate(time));
106    }
107
108    /**
109     * Convert the time stamp of the waypoint into seconds from the epoch
110     */
111    public void setTime() {
112        setTimeFromAttribute();
113    }
114
115    /**
116     * Convert the time stamp of the waypoint into seconds from the epoch
117     * @return The parsed time if successful, or {@code null}
118     * @since 9383
119     */
120    public Date setTimeFromAttribute() {
121        if (attr.containsKey(PT_TIME)) {
122            try {
123                final Date time = DateUtils.fromString(get(PT_TIME).toString());
124                this.time = time.getTime() / 1000.;
125                return time;
126            } catch (Exception e) {
127                Main.warn(e);
128                time = 0;
129            }
130        }
131        return null;
132    }
133
134    @Override
135    public int compareTo(WayPoint w) {
136        return Double.compare(time, w.time);
137    }
138
139    public Date getTime() {
140        return new Date((long) (time * 1000));
141    }
142
143    @Override
144    public Object getTemplateValue(String name, boolean special) {
145        if (!special)
146            return get(name);
147        else
148            return null;
149    }
150
151    @Override
152    public boolean evaluateCondition(Match condition) {
153        throw new UnsupportedOperationException();
154    }
155
156    @Override
157    public List<String> getTemplateKeys() {
158        return new ArrayList<>(attr.keySet());
159    }
160}