001 //License: GPLv2 or later. Copyright 2007 by Raphael Mack and others
002
003 package org.openstreetmap.josm.data.gpx;
004
005 import java.io.File;
006 import java.util.Collection;
007 import java.util.LinkedList;
008 import java.util.Map;
009
010 import org.openstreetmap.josm.data.Bounds;
011
012 /**
013 * Objects of this class represent a gpx file with tracks, waypoints and routes.
014 * It uses GPX v1.1, see {@link <a href="http://www.topografix.com/GPX/1/1/">the spec</a>}
015 * for details.
016 *
017 * @author Raphael Mack <ramack@raphael-mack.de>
018 */
019 public class GpxData extends WithAttributes {
020
021 public static final String META_PREFIX = "meta.";
022 public static final String META_AUTHOR_NAME = META_PREFIX + "author.name";
023 public static final String META_AUTHOR_EMAIL = META_PREFIX + "author.email";
024 public static final String META_AUTHOR_LINK = META_PREFIX + "author.link";
025 public static final String META_COPYRIGHT_AUTHOR = META_PREFIX + "copyright.author";
026 public static final String META_COPYRIGHT_LICENSE = META_PREFIX + "copyright.license";
027 public static final String META_COPYRIGHT_YEAR = META_PREFIX + "copyright.year";
028 public static final String META_DESC = META_PREFIX + "desc";
029 public static final String META_KEYWORDS = META_PREFIX + "keywords";
030 public static final String META_LINKS = META_PREFIX + "links";
031 public static final String META_NAME = META_PREFIX + "name";
032 public static final String META_TIME = META_PREFIX + "time";
033
034 public File storageFile;
035 public boolean fromServer;
036
037 public String creator;
038
039 public final Collection<GpxTrack> tracks = new LinkedList<GpxTrack>();
040 public final Collection<GpxRoute> routes = new LinkedList<GpxRoute>();
041 public final Collection<WayPoint> waypoints = new LinkedList<WayPoint>();
042
043 @SuppressWarnings("unchecked")
044 public void mergeFrom(GpxData other) {
045 if (storageFile == null && other.storageFile != null) {
046 storageFile = other.storageFile;
047 }
048 fromServer = fromServer && other.fromServer;
049
050 for (Map.Entry<String, Object> ent : other.attr.entrySet()) {
051 // TODO: Detect conflicts.
052 String k = ent.getKey();
053 if (k.equals(META_LINKS) && attr.containsKey(META_LINKS)) {
054 ((Collection<GpxLink>) attr.get(META_LINKS)).addAll(
055 (Collection<GpxLink>) ent.getValue());
056 } else {
057 attr.put(k, ent.getValue());
058 }
059 }
060 tracks.addAll(other.tracks);
061 routes.addAll(other.routes);
062 waypoints.addAll(other.waypoints);
063 }
064
065 public boolean hasTrackPoints() {
066 for (GpxTrack trk : tracks) {
067 for (GpxTrackSegment trkseg : trk.getSegments()) {
068 if (!trkseg.getWayPoints().isEmpty())
069 return true;
070 }
071 }
072 return false;
073 }
074
075 public boolean hasRoutePoints() {
076 for (GpxRoute rte : routes) {
077 if (!rte.routePoints.isEmpty())
078 return true;
079 }
080 return false;
081 }
082
083 public boolean isEmpty() {
084 return !hasRoutePoints() && !hasTrackPoints() && waypoints.isEmpty();
085 }
086
087 /**
088 * calculates the bounding box of available data and returns it.
089 * The bounds are not stored internally, but recalculated every time
090 * this function is called.
091 *
092 * FIXME might perhaps use visitor pattern?
093 */
094 public Bounds recalculateBounds() {
095 Bounds bounds = null;
096 for (WayPoint wpt : waypoints) {
097 if (bounds == null) {
098 bounds = new Bounds(wpt.getCoor());
099 } else {
100 bounds.extend(wpt.getCoor());
101 }
102 }
103 for (GpxRoute rte : routes) {
104 for (WayPoint wpt : rte.routePoints) {
105 if (bounds == null) {
106 bounds = new Bounds(wpt.getCoor());
107 } else {
108 bounds.extend(wpt.getCoor());
109 }
110 }
111 }
112 for (GpxTrack trk : tracks) {
113 Bounds trkBounds = trk.getBounds();
114 if (trkBounds != null) {
115 if (bounds == null) {
116 bounds = new Bounds(trkBounds);
117 } else {
118 bounds.extend(trkBounds);
119 }
120 }
121 }
122 return bounds;
123 }
124
125 /**
126 * calculates the sum of the lengths of all track segments
127 */
128 public double length(){
129 double result = 0.0; // in meters
130
131 for (GpxTrack trk : tracks) {
132 result += trk.length();
133 }
134
135 return result;
136 }
137 }