source: josm/trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java@ 5910

Last change on this file since 5910 was 5715, checked in by akks, 11 years ago

see #8416. GpxLayer refactoring: inner classes goes to org.openstreetmap.josm.gui.layer.gpx
Any change of behavior is a bug!

  • Property svn:eol-style set to native
File size: 8.5 KB
Line 
1//License: GPLv2 or later. Copyright 2007 by Raphael Mack and others
2
3package org.openstreetmap.josm.data.gpx;
4
5import java.io.File;
6import java.util.Collection;
7import java.util.LinkedList;
8import java.util.Map;
9import org.openstreetmap.josm.Main;
10
11import org.openstreetmap.josm.data.Bounds;
12import org.openstreetmap.josm.data.coor.EastNorth;
13
14/**
15 * Objects of this class represent a gpx file with tracks, waypoints and routes.
16 * It uses GPX v1.1, see {@link <a href="http://www.topografix.com/GPX/1/1/">the spec</a>}
17 * for details.
18 *
19 * @author Raphael Mack <ramack@raphael-mack.de>
20 */
21public class GpxData extends WithAttributes {
22
23 public File storageFile;
24 public boolean fromServer;
25
26 public String creator;
27
28 public final Collection<GpxTrack> tracks = new LinkedList<GpxTrack>();
29 public final Collection<GpxRoute> routes = new LinkedList<GpxRoute>();
30 public final Collection<WayPoint> waypoints = new LinkedList<WayPoint>();
31
32 @SuppressWarnings("unchecked")
33 public void mergeFrom(GpxData other) {
34 if (storageFile == null && other.storageFile != null) {
35 storageFile = other.storageFile;
36 }
37 fromServer = fromServer && other.fromServer;
38
39 for (Map.Entry<String, Object> ent : other.attr.entrySet()) {
40 // TODO: Detect conflicts.
41 String k = ent.getKey();
42 if (k.equals(META_LINKS) && attr.containsKey(META_LINKS)) {
43 ((Collection<GpxLink>) attr.get(META_LINKS)).addAll(
44 (Collection<GpxLink>) ent.getValue());
45 } else {
46 attr.put(k, ent.getValue());
47 }
48 }
49 tracks.addAll(other.tracks);
50 routes.addAll(other.routes);
51 waypoints.addAll(other.waypoints);
52 }
53
54 public boolean hasTrackPoints() {
55 for (GpxTrack trk : tracks) {
56 for (GpxTrackSegment trkseg : trk.getSegments()) {
57 if (!trkseg.getWayPoints().isEmpty())
58 return true;
59 }
60 }
61 return false;
62 }
63
64 public boolean hasRoutePoints() {
65 for (GpxRoute rte : routes) {
66 if (!rte.routePoints.isEmpty())
67 return true;
68 }
69 return false;
70 }
71
72 public boolean isEmpty() {
73 return !hasRoutePoints() && !hasTrackPoints() && waypoints.isEmpty();
74 }
75
76 /**
77 * calculates the bounding box of available data and returns it.
78 * The bounds are not stored internally, but recalculated every time
79 * this function is called.
80 *
81 * FIXME might perhaps use visitor pattern?
82 */
83 public Bounds recalculateBounds() {
84 Bounds bounds = null;
85 for (WayPoint wpt : waypoints) {
86 if (bounds == null) {
87 bounds = new Bounds(wpt.getCoor());
88 } else {
89 bounds.extend(wpt.getCoor());
90 }
91 }
92 for (GpxRoute rte : routes) {
93 for (WayPoint wpt : rte.routePoints) {
94 if (bounds == null) {
95 bounds = new Bounds(wpt.getCoor());
96 } else {
97 bounds.extend(wpt.getCoor());
98 }
99 }
100 }
101 for (GpxTrack trk : tracks) {
102 Bounds trkBounds = trk.getBounds();
103 if (trkBounds != null) {
104 if (bounds == null) {
105 bounds = new Bounds(trkBounds);
106 } else {
107 bounds.extend(trkBounds);
108 }
109 }
110 }
111 return bounds;
112 }
113
114 /**
115 * calculates the sum of the lengths of all track segments
116 */
117 public double length(){
118 double result = 0.0; // in meters
119
120 for (GpxTrack trk : tracks) {
121 result += trk.length();
122 }
123
124 return result;
125 }
126
127 /**
128 * Makes a WayPoint at the projection of point P onto the track providing P is less than
129 * tolerance away from the track
130 *
131 * @param P : the point to determine the projection for
132 * @param tolerance : must be no further than this from the track
133 * @return the closest point on the track to P, which may be the first or last point if off the
134 * end of a segment, or may be null if nothing close enough
135 */
136 public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {
137 /*
138 * assume the coordinates of P are xp,yp, and those of a section of track between two
139 * trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.
140 *
141 * The equation of RS is Ax + By + C = 0 where A = ys - yr B = xr - xs C = - Axr - Byr
142 *
143 * Also, note that the distance RS^2 is A^2 + B^2
144 *
145 * If RS^2 == 0.0 ignore the degenerate section of track
146 *
147 * PN^2 = (Axp + Byp + C)^2 / RS^2 that is the distance from P to the line
148 *
149 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject the line;
150 * otherwise... determine if the projected poijnt lies within the bounds of the line: PR^2 -
151 * PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2
152 *
153 * where PR^2 = (xp - xr)^2 + (yp-yr)^2 and PS^2 = (xp - xs)^2 + (yp-ys)^2
154 *
155 * If so, calculate N as xn = xr + (RN/RS) B yn = y1 + (RN/RS) A
156 *
157 * where RN = sqrt(PR^2 - PN^2)
158 */
159
160 double PNminsq = tolerance * tolerance;
161 EastNorth bestEN = null;
162 double bestTime = 0.0;
163 double px = P.east();
164 double py = P.north();
165 double rx = 0.0, ry = 0.0, sx, sy, x, y;
166 if (tracks == null)
167 return null;
168 for (GpxTrack track : tracks) {
169 for (GpxTrackSegment seg : track.getSegments()) {
170 WayPoint R = null;
171 for (WayPoint S : seg.getWayPoints()) {
172 EastNorth c = S.getEastNorth();
173 if (R == null) {
174 R = S;
175 rx = c.east();
176 ry = c.north();
177 x = px - rx;
178 y = py - ry;
179 double PRsq = x * x + y * y;
180 if (PRsq < PNminsq) {
181 PNminsq = PRsq;
182 bestEN = c;
183 bestTime = R.time;
184 }
185 } else {
186 sx = c.east();
187 sy = c.north();
188 double A = sy - ry;
189 double B = rx - sx;
190 double C = -A * rx - B * ry;
191 double RSsq = A * A + B * B;
192 if (RSsq == 0.0) {
193 continue;
194 }
195 double PNsq = A * px + B * py + C;
196 PNsq = PNsq * PNsq / RSsq;
197 if (PNsq < PNminsq) {
198 x = px - rx;
199 y = py - ry;
200 double PRsq = x * x + y * y;
201 x = px - sx;
202 y = py - sy;
203 double PSsq = x * x + y * y;
204 if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {
205 double RNoverRS = Math.sqrt((PRsq - PNsq) / RSsq);
206 double nx = rx - RNoverRS * B;
207 double ny = ry + RNoverRS * A;
208 bestEN = new EastNorth(nx, ny);
209 bestTime = R.time + RNoverRS * (S.time - R.time);
210 PNminsq = PNsq;
211 }
212 }
213 R = S;
214 rx = sx;
215 ry = sy;
216 }
217 }
218 if (R != null) {
219 EastNorth c = R.getEastNorth();
220 /* if there is only one point in the seg, it will do this twice, but no matter */
221 rx = c.east();
222 ry = c.north();
223 x = px - rx;
224 y = py - ry;
225 double PRsq = x * x + y * y;
226 if (PRsq < PNminsq) {
227 PNminsq = PRsq;
228 bestEN = c;
229 bestTime = R.time;
230 }
231 }
232 }
233 }
234 if (bestEN == null)
235 return null;
236 WayPoint best = new WayPoint(Main.getProjection().eastNorth2latlon(bestEN));
237 best.time = bestTime;
238 return best;
239 }
240}
Note: See TracBrowser for help on using the repository browser.