TCX::writeTrackPoints()   B
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 19
nc 3
nop 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace SportTrackerConnector\Core\Workout\Dumper;
6
7
use SportTrackerConnector\Core\Workout\Extension\ExtensionInterface;
8
use SportTrackerConnector\Core\Workout\Extension\HR;
9
use SportTrackerConnector\Core\Workout\Track;
10
use SportTrackerConnector\Core\Workout\TrackPoint;
11
use SportTrackerConnector\Core\Workout\Workout;
12
13
/**
14
 * Dump a workout to TCX format.
15
 */
16
class TCX implements DumperInterface
17
{
18
    /**
19
     * {@inheritdoc}
20
     */
21
    public function dump(Workout $workout): string
22
    {
23
        $xmlWriter = new \XMLWriter();
24
        $xmlWriter->openMemory();
25
        $xmlWriter->setIndent(true);
26
        $xmlWriter->startDocument('1.0', 'UTF-8');
27
        $xmlWriter->startElement('TrainingCenterDatabase');
28
29
        $xmlWriter->writeAttributeNS(
30
            'xsi',
31
            'schemaLocation',
32
            null,
33
            'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd'
34
        );
35
        $xmlWriter->writeAttribute('xmlns', 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2');
36
        $xmlWriter->writeAttributeNS('xmlns', 'xsi', null, 'http://www.w3.org/2001/XMLSchema-instance');
37
38
        $this->writeTracks($xmlWriter, $workout);
39
40
        $xmlWriter->endElement();
41
        $xmlWriter->endDocument();
42
43
        return $xmlWriter->outputMemory(true);
44
    }
45
46
    /**
47
     * Write the tracks to the TCX.
48
     *
49
     * @param \XMLWriter $xmlWriter The XML writer.
50
     * @param Workout $workout The workout.
51
     */
52
    private function writeTracks(\XMLWriter $xmlWriter, Workout $workout)
53
    {
54
        $xmlWriter->startElement('Activities');
55
        foreach ($workout->tracks() as $track) {
56
            $xmlWriter->startElement('Activity');
57
            $xmlWriter->writeAttribute('Sport', ucfirst($track->sport()));
58
            // Use the start date time as the ID. This could be anything.
59
            $xmlWriter->writeElement('Id', $this->formatDateTime($track->startDateTime()));
60
61
            $xmlWriter->startElement('Lap');
62
63
            $xmlWriter->writeAttribute('StartTime', $this->formatDateTime($track->startDateTime()));
64
            $xmlWriter->writeElement('TotalTimeSeconds', (string)$track->duration()->totalSeconds());
65
            $xmlWriter->writeElement('DistanceMeters', (string)$track->length());
66
67
            $this->writeLapHeartRateDate($xmlWriter, $track);
68
69
            $xmlWriter->startElement('Track');
70
            $this->writeTrackPoints($xmlWriter, $track->trackPoints());
71
            $xmlWriter->endElement();
72
73
            $xmlWriter->endElement();
74
75
            $xmlWriter->endElement();
76
        }
77
        $xmlWriter->endElement();
78
    }
79
80
    /**
81
     * Write the track points to the TCX.
82
     *
83
     * @param \XMLWriter $xmlWriter The XML writer.
84
     * @param TrackPoint[] $trackPoints The track points to write.
85
     */
86
    private function writeTrackPoints(\XMLWriter $xmlWriter, array $trackPoints)
87
    {
88
        $previousTrackPoint = null;
89
        foreach ($trackPoints as $trackPoint) {
90
            $xmlWriter->startElement('Trackpoint');
91
92
            // Time of position
93
            $dateTime = clone $trackPoint->dateTime();
94
            $dateTime->setTimezone(new \DateTimeZone('UTC'));
95
            $xmlWriter->writeElement('Time', $this->formatDateTime($dateTime));
96
97
            // Position.
98
            $xmlWriter->startElement('Position');
99
            $xmlWriter->writeElement('LatitudeDegrees', (string)$trackPoint->latitude());
100
            $xmlWriter->writeElement('LongitudeDegrees', (string)$trackPoint->longitude());
101
            $xmlWriter->endElement();
102
103
            // Elevation.
104
            $xmlWriter->writeElement('AltitudeMeters', (string)$trackPoint->elevation());
105
106
            // Distance.
107
            if ($previousTrackPoint !== null) {
108
                $xmlWriter->writeElement('DistanceMeters', (string)$trackPoint->distanceFromPoint($previousTrackPoint));
109
            } else {
110
                $xmlWriter->writeElement('DistanceMeters', '0');
111
            }
112
113
            // Extensions.
114
            $this->writeExtensions($xmlWriter, $trackPoint->extensions());
115
116
            $xmlWriter->endElement();
117
118
            $previousTrackPoint = $trackPoint;
119
        }
120
    }
121
122
    /**
123
     * Write the heart rate data for a lap.
124
     *
125
     * @param \XMLWriter $xmlWriter The XML writer.
126
     * @param Track $track The track to write.
127
     */
128
    protected function writeLapHeartRateDate(\XMLWriter $xmlWriter, Track $track)
129
    {
130
        $averageHeartRate = array();
131
        $maxHearRate = null;
132
        foreach ($track->trackPoints() as $trackPoint) {
133
            if ($trackPoint->hasExtension(HR::ID()) === true) {
134
                $pointHearRate = $trackPoint->extension(HR::ID())->value();
135
136
                $maxHearRate = max($maxHearRate, $pointHearRate);
137
                $averageHeartRate[] = $pointHearRate;
138
            }
139
        }
140
141
        if ($averageHeartRate !== array()) {
142
            $xmlWriter->startElement('AverageHeartRateBpm');
143
            $xmlWriter->writeAttributeNS('xsi', 'type', null, 'HeartRateInBeatsPerMinute_t');
144
            $hearRateValue = array_sum($averageHeartRate) / count($averageHeartRate);
145
            $xmlWriter->writeElement('Value', (string)$hearRateValue);
146
            $xmlWriter->endElement();
147
        }
148
149
        if ($maxHearRate !== null) {
150
            $xmlWriter->startElement('MaximumHeartRateBpm');
151
            $xmlWriter->writeAttributeNS('xsi', 'type', null, 'HeartRateInBeatsPerMinute_t');
152
            $xmlWriter->writeElement('Value', (string)$maxHearRate);
153
            $xmlWriter->endElement();
154
        }
155
    }
156
157
    /**
158
     * Write the extensions into the TCX.
159
     *
160
     * @param \XMLWriter $xmlWriter The XMLWriter.
161
     * @param ExtensionInterface[] $extensions The extensions to write.
162
     */
163
    protected function writeExtensions(\XMLWriter $xmlWriter, array $extensions)
164
    {
165
        foreach ($extensions as $extension) {
166
            switch ($extension::ID()) {
167
                case HR::ID():
168
                    $xmlWriter->startElement('HeartRateBpm');
169
                    $xmlWriter->writeElement('Value', (string)$extension->value());
170
                    $xmlWriter->endElement();
171
                    break;
172
            }
173
        }
174
    }
175
176
    /**
177
     * Format a DateTime object for TCX format.
178
     *
179
     * @param \DateTimeImmutable $dateTime The date time to format.
180
     * @return string
181
     */
182
    protected function formatDateTime(\DateTimeImmutable $dateTime): string
183
    {
184
        return $dateTime->format('Y-m-d\TH:i:s\Z');
185
    }
186
}
187