-
Notifications
You must be signed in to change notification settings - Fork 0
/
TLGPXFileConversion.m
162 lines (142 loc) · 6 KB
/
TLGPXFileConversion.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//
// TLGPXFileConversion.m
// Tagalog
//
// Created by Nathan Vander Wilt on 4/23/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
//
#import "TLGPXFileConversion.h"
#import "TLTrack.h"
#import "TLWaypoint.h"
#import "TLLocation.h"
#import "TLTimestamp.h"
static const NSTimeInterval TLTimestampBaseGPSAccuracy = 0.5;
static const TLCoordinateAccuracy TLCoordinateBaseGPSAccuracy = 1.2;
TLCoordinateAccuracy TLCoordinateHorizontalAccuracy(double horizontalDOP, double positionDOP);
TLCoordinateAccuracy TLCoordinateVerticalAccuracy(double verticalDOP, double positionDOP);
@implementation TLGPXFile (TLGPXFileConversion)
static NSComparisonResult TLTrackSortTrackpoints(id trackpointA, id trackpointB, void* ctx) {
(void)ctx;
return [[(TLGPXWaypoint*)trackpointA time] compare:[(TLGPXWaypoint*)trackpointB time]];
}
+ (NSArray*)waypointsFromTrackSegment:(TLGPXTrackSegment*)trackSegment error:(NSError**)err {
NSUInteger numTrackpoints = [[trackSegment trackpoints] count];
if (!numTrackpoints) {
if (err) {
NSString* errorString = NSLocalizedString(@"GPX track segment must have at least one point",
@"Error message when track segment contains no points");
NSDictionary* errorInfo = [NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey];
*err = [NSError errorWithDomain:@"com.calftrail.mercatalog" code:42 userInfo:errorInfo];
}
return nil;
}
// some sources do not have "properly" ordered trackpoints
NSArray* sortedTrackpoints = [[trackSegment trackpoints]
sortedArrayUsingFunction:TLTrackSortTrackpoints context:NULL];
NSMutableArray* mutableWaypoints = [NSMutableArray array];
NSDate* previousDate = nil;
for (TLGPXWaypoint* trackpoint in sortedTrackpoints) {
// some sources have imprecise timestamps yielding identical times
NSDate* date = [trackpoint time];
if (!date) {
continue;
}
if (previousDate && ![date isGreaterThan:previousDate]) {
continue;
}
previousDate = date;
TLCoordinateAccuracy hAcc = TLCoordinateHorizontalAccuracy([trackpoint horizontalDOP],
[trackpoint positionDOP]);
TLCoordinateAccuracy vAcc = TLCoordinateVerticalAccuracy([trackpoint verticalDOP],
[trackpoint positionDOP]);
TLLocation* location = [TLLocation locationWithCoordinate:[trackpoint coordinate]
horizontalAccuracy:hAcc
altitude:[trackpoint elevation]
verticalAccuracy:vAcc];
TLTimestamp* timestamp = [TLTimestamp timestampWithTime:[trackpoint time]
accuracy:TLTimestampBaseGPSAccuracy];
TLWaypoint* waypoint = [TLWaypoint waypointWithLocation:location timestamp:timestamp];
[mutableWaypoints addObject:waypoint];
}
if ([mutableWaypoints count] < [sortedTrackpoints count] && [mutableWaypoints count] < 2) {
if (err) {
NSString* errorString = NSLocalizedString(@"Track segment does not contain enough valid points. "
@"This can happen when the original timestamps have been discarded.",
@"Error message when GPX trackpoints have been discarded");
NSDictionary* errorInfo = [NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey];
*err = [NSError errorWithDomain:@"com.calftrail.mercatalog" code:42 userInfo:errorInfo];
}
return nil;
}
return mutableWaypoints;
}
- (NSArray*)extractTracks:(NSError**)err {
NSMutableArray* errors = [NSMutableArray array];
NSMutableArray* extractedTracks = [NSMutableArray array];
for (TLGPXTracklog* gpxTracklog in [self tracks]) {
for (TLGPXTrackSegment* segment in gpxTracklog) {
NSError* internalError;
NSArray* extractedWaypoints = [[self class] waypointsFromTrackSegment:segment error:&internalError];
if (!extractedWaypoints) {
[errors addObject:internalError];
continue;
}
TLTrack* track = [[TLTrack alloc] initWithWaypoints:extractedWaypoints];
[extractedTracks addObject:track];
[track release];
}
}
if ([errors count]) {
NSLog(@"Errors loading track: %@\n", errors);
}
if (![extractedTracks count]) {
if (err) {
NSDictionary* errInfo = [NSDictionary dictionaryWithObject:
@"No valid tracks were found in the tracklog."
forKey:NSLocalizedDescriptionKey];
*err = [NSError errorWithDomain:@"com.calftrail.tagalog" code:-41 userInfo:errInfo];
}
return nil;
}
return extractedTracks;
}
- (NSArray*)extractWaypoints:(NSError**)err {
(void)err;
NSMutableArray* extractedWaypoints = [NSMutableArray array];
for (TLGPXWaypoint* gpxWaypoint in [self waypoints]) {
TLLocation* location = [TLLocation locationWithCoordinate:[gpxWaypoint coordinate]
horizontalAccuracy:TLCoordinateAccuracyUnknown];
[extractedWaypoints addObject:location];
}
return extractedWaypoints;
}
@end
TLCoordinateAccuracy TLCoordinateHorizontalAccuracy(double horizontalDOP, double positionDOP) {
TLCoordinateAccuracy horizontalAccuracy = TLCoordinateAccuracyUnknown;
if (horizontalDOP) {
horizontalAccuracy = TLCoordinateBaseGPSAccuracy * horizontalDOP;
}
else if (positionDOP) {
/* Horizontal typically contributes significantly less dilution.
This factor is based on values from AMOD tracker where hdop=1.2, vdop=1.7 and pdop=2.1 */
const double horizontalComponentFactor = 0.33;
double presumedHorizontalDOP = sqrt(horizontalComponentFactor * (positionDOP * positionDOP));
horizontalAccuracy = TLCoordinateBaseGPSAccuracy * presumedHorizontalDOP;
}
return horizontalAccuracy;
}
TLCoordinateAccuracy TLCoordinateVerticalAccuracy(double verticalDOP, double positionDOP) {
TLCoordinateAccuracy verticalAccuracy = TLCoordinateAccuracyUnknown;
if (verticalDOP) {
verticalAccuracy = TLCoordinateBaseGPSAccuracy * verticalDOP;
}
else if (positionDOP) {
// see note in TLCoordinateHorizontalAccuracy regarding this factor
const double verticalComponentFactor = 0.66;
double presumedVerticalDOP = sqrt(verticalComponentFactor * (positionDOP * positionDOP));
verticalAccuracy = TLCoordinateBaseGPSAccuracy * presumedVerticalDOP;
}
return verticalAccuracy;
}