// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.data.gpx;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.projection.Projections;
import org.openstreetmap.josm.tools.UncheckedParseException;
import org.openstreetmap.josm.tools.date.DateUtils;
import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider;
public class WayPoint extends WithAttributes implements Comparable<WayPoint>, TemplateEngineDataProvider {
/**
* The seconds (not milliseconds!) since 1970-01-01 00:00 UTC
*/
public double time;
public Color customColoring;
public boolean drawLine;
public int dir;
public WayPoint(WayPoint p) {
attr.putAll(p.attr);
lat = p.lat;
lon = p.lon;
east = p.east;
north = p.north;
time = p.time;
customColoring = p.customColoring;
drawLine = p.drawLine;
dir = p.dir;
}
public WayPoint(LatLon ll) {
lat = ll.lat();
lon = ll.lon();
}
/*
* We "inline" lat/lon, rather than usinga LatLon internally => reduces memory overhead. Relevant
* because a lot of GPX waypoints are created when GPS tracks are downloaded from the OSM server.
*/
private final double lat;
private final double lon;
/*
* internal cache of projected coordinates
*/
private double east = Double.NaN;
private double north = Double.NaN;
/**
* Invalidate the internal cache of east/north coordinates.
*/
public void invalidateEastNorthCache() {
this.east = Double.NaN;
this.north = Double.NaN;
}
public final LatLon getCoor() {
return new LatLon(lat, lon);
}
/**
* <p>Replies the projected east/north coordinates.</p>
*
* <p>Uses the {@link Main#getProjection() global projection} to project the lan/lon-coordinates.
* Internally caches the projected coordinates.</p>
*
* <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must
* {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>
*
* @return the east north coordinates or {@code null}
* @see #invalidateEastNorthCache()
*/
public final EastNorth getEastNorth() {
if (Double.isNaN(east) || Double.isNaN(north)) {
// projected coordinates haven't been calculated yet,
// so fill the cache of the projected waypoint coordinates
EastNorth en = Projections.project(new LatLon(lat, lon));
this.east = en.east();
this.north = en.north();
}
return new EastNorth(east, north);
}
@Override
public String toString() {
return "WayPoint (" + (attr.containsKey(GPX_NAME) ? get(GPX_NAME) + ", " : "") + getCoor() + ", " + attr + ')';
}
/**
* Sets the {@link #time} field as well as the {@link #PT_TIME} attribute to the specified time
*
* @param time the time to set
* @since 9383
*/
public void setTime(Date time) {
this.time = time.getTime() / 1000.;
this.attr.put(PT_TIME, DateUtils.fromDate(time));
}
/**
* Convert the time stamp of the waypoint into seconds from the epoch
*/
public void setTime() {
setTimeFromAttribute();
}
/**
* Convert the time stamp of the waypoint into seconds from the epoch
* @return The parsed time if successful, or {@code null}
* @since 9383
*/
public Date setTimeFromAttribute() {
if (attr.containsKey(PT_TIME)) {
try {
final Date time = DateUtils.fromString(get(PT_TIME).toString());
this.time = time.getTime() / 1000.;
return time;
} catch (UncheckedParseException e) {
Main.warn(e);
time = 0;
}
}
return null;
}
@Override
public int compareTo(WayPoint w) {
return Double.compare(time, w.time);
}
public Date getTime() {
return new Date((long) (time * 1000));
}
@Override
public Object getTemplateValue(String name, boolean special) {
if (!special)
return get(name);
else
return null;
}
@Override
public boolean evaluateCondition(Match condition) {
throw new UnsupportedOperationException();
}
@Override
public List<String> getTemplateKeys() {
return new ArrayList<>(attr.keySet());
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
long temp = Double.doubleToLongBits(lat);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(lon);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(time);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
WayPoint other = (WayPoint) obj;
if (Double.doubleToLongBits(lat) != Double.doubleToLongBits(other.lat))
return false;
if (Double.doubleToLongBits(lon) != Double.doubleToLongBits(other.lon))
return false;
if (Double.doubleToLongBits(time) != Double.doubleToLongBits(other.time))
return false;
return true;
}
}