1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types = 1); |
4
|
|
|
|
5
|
|
|
namespace SportTrackerConnector\Core\Workout; |
6
|
|
|
|
7
|
|
|
use Assert\Assertion; |
8
|
|
|
use SportTrackerConnector\Core\Date\DateInterval; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* A track of a workout. |
12
|
|
|
*/ |
13
|
|
|
final class Track |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* The sport for the workout. |
17
|
|
|
* |
18
|
|
|
* @var string |
19
|
|
|
*/ |
20
|
|
|
private $sport = SportMapperInterface::OTHER; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* The track points of this track. |
24
|
|
|
* |
25
|
|
|
* @var TrackPoint[] |
26
|
|
|
*/ |
27
|
|
|
private $trackPoints = array(); |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* The start date and time of the track. |
31
|
|
|
* |
32
|
|
|
* @var \DateTimeImmutable |
33
|
|
|
*/ |
34
|
|
|
private $startDateTime; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* The end date and time of the track. |
38
|
|
|
* |
39
|
|
|
* @var \DateTimeImmutable |
40
|
|
|
*/ |
41
|
|
|
private $endDateTime; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Get the length of the track in meters. |
45
|
|
|
* |
46
|
|
|
* @var float |
47
|
|
|
*/ |
48
|
|
|
private $length = 0; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Constructor. |
52
|
|
|
* |
53
|
|
|
* @param TrackPoint[] $trackPoints The track points. |
54
|
|
|
* @param string $sport The sport for this track. |
55
|
|
|
*/ |
56
|
|
|
public function __construct(array $trackPoints = array(), string $sport = SportMapperInterface::OTHER) |
57
|
|
|
{ |
58
|
|
|
Assertion::allIsInstanceOf($trackPoints, TrackPoint::class); |
59
|
|
|
|
60
|
|
|
$this->trackPoints = $trackPoints; |
61
|
|
|
$this->sport = $sport; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Get the sport of the workout. |
66
|
|
|
* |
67
|
|
|
* @return string |
68
|
|
|
*/ |
69
|
|
|
public function sport(): string |
70
|
|
|
{ |
71
|
|
|
return $this->sport; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Get the track points. |
76
|
|
|
* |
77
|
|
|
* @return TrackPoint[] |
78
|
|
|
*/ |
79
|
|
|
public function trackPoints() |
80
|
|
|
{ |
81
|
|
|
return $this->trackPoints; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Get the last track point. |
86
|
|
|
* |
87
|
|
|
* @return TrackPoint |
88
|
|
|
* @throws \OutOfBoundsException If no track points are defined. |
89
|
|
|
*/ |
90
|
|
|
public function lastTrackPoint(): TrackPoint |
91
|
|
|
{ |
92
|
|
|
$lastTrackPoint = end($this->trackPoints); |
93
|
|
|
|
94
|
|
|
if (!$lastTrackPoint instanceof TrackPoint) { |
95
|
|
|
throw new \OutOfBoundsException('No track points points defined.'); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
reset($this->trackPoints); |
99
|
|
|
|
100
|
|
|
return $lastTrackPoint; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Get the start date and time of the track. |
105
|
|
|
* |
106
|
|
|
* @return \DateTimeImmutable |
107
|
|
|
*/ |
108
|
|
|
public function startDateTime(): \DateTimeImmutable |
109
|
|
|
{ |
110
|
|
|
if ($this->startDateTime === null) { |
111
|
|
|
$this->recomputeStartDateTime(); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
return $this->startDateTime; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Recompute the start date and time of the track. |
119
|
|
|
* |
120
|
|
|
* @return \DateTimeImmutable |
121
|
|
|
*/ |
122
|
|
View Code Duplication |
private function recomputeStartDateTime(): \DateTimeImmutable |
|
|
|
|
123
|
|
|
{ |
124
|
|
|
$this->startDateTime = null; |
125
|
|
|
foreach ($this->trackPoints() as $trackPoint) { |
126
|
|
|
if ($this->startDateTime === null || $this->startDateTime > $trackPoint->dateTime()) { |
127
|
|
|
$this->startDateTime = clone $trackPoint->dateTime(); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
return $this->startDateTime; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Get the start date and time of the track. |
136
|
|
|
* |
137
|
|
|
* @return \DateTimeImmutable |
138
|
|
|
*/ |
139
|
|
|
public function endDateTime(): \DateTimeImmutable |
140
|
|
|
{ |
141
|
|
|
if ($this->endDateTime === null) { |
142
|
|
|
$this->recomputeEndDateTime(); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
return $this->endDateTime; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Recompute the start date and time of the track. |
150
|
|
|
* |
151
|
|
|
* @return \DateTimeImmutable |
152
|
|
|
*/ |
153
|
|
View Code Duplication |
private function recomputeEndDateTime(): \DateTimeImmutable |
|
|
|
|
154
|
|
|
{ |
155
|
|
|
$this->endDateTime = null; |
156
|
|
|
foreach ($this->trackPoints() as $trackPoint) { |
157
|
|
|
if ($this->startDateTime === null || $this->endDateTime < $trackPoint->dateTime()) { |
158
|
|
|
$this->endDateTime = clone $trackPoint->dateTime(); |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
return $this->endDateTime; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Get the duration of the track. |
167
|
|
|
* |
168
|
|
|
* @return DateInterval |
169
|
|
|
*/ |
170
|
|
|
public function duration() |
171
|
|
|
{ |
172
|
|
|
$start = $this->startDateTime(); |
173
|
|
|
$end = $this->endDateTime(); |
174
|
|
|
|
175
|
|
|
$dateDifference = $start->diff($end); |
176
|
|
|
|
177
|
|
|
$dateInterval = new DateInterval('PT1S'); |
178
|
|
|
$dateInterval->y = $dateDifference->y; |
179
|
|
|
$dateInterval->m = $dateDifference->m; |
180
|
|
|
$dateInterval->d = $dateDifference->d; |
181
|
|
|
$dateInterval->h = $dateDifference->h; |
182
|
|
|
$dateInterval->i = $dateDifference->i; |
183
|
|
|
$dateInterval->s = $dateDifference->s; |
184
|
|
|
$dateInterval->invert = $dateDifference->invert; |
185
|
|
|
$dateInterval->days = $dateDifference->days; |
186
|
|
|
|
187
|
|
|
return $dateInterval; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Set the length of the track in meters. |
192
|
|
|
* |
193
|
|
|
* @param float $length The length of the track in meters. |
194
|
|
|
* @deprecated |
195
|
|
|
*/ |
196
|
|
|
public function setLength(float $length) |
197
|
|
|
{ |
198
|
|
|
$this->length = $length; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Get the length of the track in meters. |
203
|
|
|
* |
204
|
|
|
* @return float |
205
|
|
|
*/ |
206
|
|
|
public function length(): float |
207
|
|
|
{ |
208
|
|
|
if ($this->length === 0) { |
209
|
|
|
$this->length = $this->recomputeLength(); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
return $this->length; |
213
|
|
|
|
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Recompute the length of the track. |
218
|
|
|
* |
219
|
|
|
* @return float |
220
|
|
|
*/ |
221
|
|
|
private function recomputeLength(): float |
222
|
|
|
{ |
223
|
|
|
$this->length = 0; |
|
|
|
|
224
|
|
|
|
225
|
|
|
$trackPoints = $this->trackPoints(); |
226
|
|
|
$trackPointsCount = count($trackPoints); |
227
|
|
|
if ($trackPointsCount < 2) { |
228
|
|
|
return 0; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
for ($i = 1; $i < $trackPointsCount; $i++) { |
232
|
|
|
$previousTrack = $trackPoints[$i - 1]; |
233
|
|
|
$currentTrack = $trackPoints[$i]; |
234
|
|
|
|
235
|
|
|
$this->length += $currentTrack->distanceFromPoint($previousTrack); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
$this->length = round($this->length, 6); |
239
|
|
|
|
240
|
|
|
return $this->length; |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.