| 1 | //License: GPLv2 or later. Copyright 2007 by Raphael Mack and others |
|---|
| 2 | |
|---|
| 3 | package org.openstreetmap.josm.data.gpx; |
|---|
| 4 | |
|---|
| 5 | import java.io.File; |
|---|
| 6 | import java.util.Collection; |
|---|
| 7 | import java.util.LinkedList; |
|---|
| 8 | import java.util.Map; |
|---|
| 9 | |
|---|
| 10 | import org.openstreetmap.josm.data.Bounds; |
|---|
| 11 | |
|---|
| 12 | /** |
|---|
| 13 | * Objects of this class represent a gpx file with tracks, waypoints and routes. |
|---|
| 14 | * It uses GPX v1.1, see {@link <a href="http://www.topografix.com/GPX/1/1/">the spec</a>} |
|---|
| 15 | * for details. |
|---|
| 16 | * |
|---|
| 17 | * @author Raphael Mack <ramack@raphael-mack.de> |
|---|
| 18 | */ |
|---|
| 19 | public class GpxData extends WithAttributes { |
|---|
| 20 | |
|---|
| 21 | public static final String META_PREFIX = "meta."; |
|---|
| 22 | public static final String META_AUTHOR_NAME = META_PREFIX + "author.name"; |
|---|
| 23 | public static final String META_AUTHOR_EMAIL = META_PREFIX + "author.email"; |
|---|
| 24 | public static final String META_AUTHOR_LINK = META_PREFIX + "author.link"; |
|---|
| 25 | public static final String META_COPYRIGHT_AUTHOR = META_PREFIX + "copyright.author"; |
|---|
| 26 | public static final String META_COPYRIGHT_LICENSE = META_PREFIX + "copyright.license"; |
|---|
| 27 | public static final String META_COPYRIGHT_YEAR = META_PREFIX + "copyright.year"; |
|---|
| 28 | public static final String META_DESC = META_PREFIX + "desc"; |
|---|
| 29 | public static final String META_KEYWORDS = META_PREFIX + "keywords"; |
|---|
| 30 | public static final String META_LINKS = META_PREFIX + "links"; |
|---|
| 31 | public static final String META_NAME = META_PREFIX + "name"; |
|---|
| 32 | public static final String META_TIME = META_PREFIX + "time"; |
|---|
| 33 | |
|---|
| 34 | public File storageFile; |
|---|
| 35 | public boolean fromServer; |
|---|
| 36 | |
|---|
| 37 | public final Collection<GpxTrack> tracks = new LinkedList<GpxTrack>(); |
|---|
| 38 | public final Collection<GpxRoute> routes = new LinkedList<GpxRoute>(); |
|---|
| 39 | public final Collection<WayPoint> waypoints = new LinkedList<WayPoint>(); |
|---|
| 40 | |
|---|
| 41 | @SuppressWarnings("unchecked") |
|---|
| 42 | public void mergeFrom(GpxData other) { |
|---|
| 43 | if (storageFile == null && other.storageFile != null) { |
|---|
| 44 | storageFile = other.storageFile; |
|---|
| 45 | } |
|---|
| 46 | fromServer = fromServer && other.fromServer; |
|---|
| 47 | |
|---|
| 48 | for (Map.Entry<String, Object> ent : other.attr.entrySet()) { |
|---|
| 49 | // TODO: Detect conflicts. |
|---|
| 50 | String k = ent.getKey(); |
|---|
| 51 | if (k.equals(META_LINKS) && attr.containsKey(META_LINKS)) { |
|---|
| 52 | ((Collection<GpxLink>) attr.get(META_LINKS)).addAll( |
|---|
| 53 | (Collection<GpxLink>) ent.getValue()); |
|---|
| 54 | } else { |
|---|
| 55 | attr.put(k, ent.getValue()); |
|---|
| 56 | } |
|---|
| 57 | } |
|---|
| 58 | tracks.addAll(other.tracks); |
|---|
| 59 | routes.addAll(other.routes); |
|---|
| 60 | waypoints.addAll(other.waypoints); |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | public boolean hasTrackPoints() { |
|---|
| 64 | for (GpxTrack trk : tracks) { |
|---|
| 65 | for (GpxTrackSegment trkseg : trk.getSegments()) { |
|---|
| 66 | if (!trkseg.getWayPoints().isEmpty()) |
|---|
| 67 | return true; |
|---|
| 68 | } |
|---|
| 69 | } |
|---|
| 70 | return false; |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | public boolean hasRoutePoints() { |
|---|
| 74 | for (GpxRoute rte : routes) { |
|---|
| 75 | if (!rte.routePoints.isEmpty()) |
|---|
| 76 | return true; |
|---|
| 77 | } |
|---|
| 78 | return false; |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | public boolean isEmpty() { |
|---|
| 82 | return !hasRoutePoints() && !hasTrackPoints() && waypoints.isEmpty(); |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | /** |
|---|
| 86 | * calculates the bounding box of available data and returns it. |
|---|
| 87 | * The bounds are not stored internally, but recalculated every time |
|---|
| 88 | * this function is called. |
|---|
| 89 | * |
|---|
| 90 | * FIXME might perhaps use visitor pattern? |
|---|
| 91 | */ |
|---|
| 92 | public Bounds recalculateBounds() { |
|---|
| 93 | Bounds bounds = null; |
|---|
| 94 | for (WayPoint wpt : waypoints) { |
|---|
| 95 | if (bounds == null) { |
|---|
| 96 | bounds = new Bounds(wpt.getCoor()); |
|---|
| 97 | } else { |
|---|
| 98 | bounds.extend(wpt.getCoor()); |
|---|
| 99 | } |
|---|
| 100 | } |
|---|
| 101 | for (GpxRoute rte : routes) { |
|---|
| 102 | for (WayPoint wpt : rte.routePoints) { |
|---|
| 103 | if (bounds == null) { |
|---|
| 104 | bounds = new Bounds(wpt.getCoor()); |
|---|
| 105 | } else { |
|---|
| 106 | bounds.extend(wpt.getCoor()); |
|---|
| 107 | } |
|---|
| 108 | } |
|---|
| 109 | } |
|---|
| 110 | for (GpxTrack trk : tracks) { |
|---|
| 111 | Bounds trkBounds = trk.getBounds(); |
|---|
| 112 | if (trkBounds != null) { |
|---|
| 113 | if (bounds == null) { |
|---|
| 114 | bounds = new Bounds(trkBounds); |
|---|
| 115 | } else { |
|---|
| 116 | bounds.extend(trkBounds); |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | } |
|---|
| 120 | return bounds; |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | /** |
|---|
| 124 | * calculates the sum of the lengths of all track segments |
|---|
| 125 | */ |
|---|
| 126 | public double length(){ |
|---|
| 127 | double result = 0.0; // in meters |
|---|
| 128 | |
|---|
| 129 | for (GpxTrack trk : tracks) { |
|---|
| 130 | result += trk.length(); |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | return result; |
|---|
| 134 | } |
|---|
| 135 | } |
|---|