Completed
Pull Request — master (#67)
by Matt
08:42
created

GpxService::getGpxStringFromFilename()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
namespace App\Service;
4
5
use App\Entity\Wander;
6
use Exception;
7
use phpGPX\phpGPX;
8
use Psr\Log\LoggerInterface;
9
10
class GpxService
11
{
12
    /** @var phpGPX */
13
    private $phpGpx;
14
    /** @var string */
15
    private $gpxDirectory;
16
    /** @var LoggerInterface */
17
    private $logger;
18
    /** @var array */
19
    private $homebaseCoords;
20
21
    public function __construct(string $gpxDirectory, LoggerInterface $logger, array $homebaseCoords)
22
    {
23
        $this->phpGpx = new phpGPX();
24
        $this->gpxDirectory = $gpxDirectory;
25
        $this->logger = $logger;
26
        $this->homebaseCoords = $homebaseCoords;
27
    }
28
29
    /**
30
     * @throws \Exception
31
     */
32
    public function getFullGpxFilePathFromWander(Wander $wander): string
33
    {
34
        $filename = $wander->getGpxFilename();
35
        if (!$filename) {
36
            throw new \Exception("No GPX file path found in wander.");
37
        }
38
        return $this->getFullGpxFilePathFromFilename($filename);
39
    }
40
41
    public function getFullGpxFilePathFromFilename(string $filename): string
42
    {
43
        return $this->gpxDirectory . '/' . $filename;
44
    }
45
46
    public function getGpxStringFromFilename(string $filename): string
47
    {
48
        $gpx = \file_get_contents($this->getFullGpxFilePathFromFilename($filename));
49
        if ($gpx === false) {
50
            throw new Exception("Couldn't read GPX file from $filename");
51
        }
52
        return $gpx;
53
    }
54
55
    private function updateGeneralStats(string $gpxxml, Wander $wander): void
56
    {
57
        $gpx = $this->phpGpx->parse($gpxxml);
58
        // TODO: Cope with multiple tracks in a file? I don't think
59
        // we've done that often, if ever.
60
        foreach ($gpx->tracks as $track)
61
        {
62
            $stats = $track->stats;
63
            // These are the only essentials
64
            $wander->setStartTime($stats->startedAt);
65
            $wander->setEndTime($stats->finishedAt);
66
            try {
67
                $wander->setDistance($stats->distance);
68
                $wander->setAvgPace($stats->averagePace);
69
                $wander->setAvgSpeed($stats->averageSpeed);
70
                $wander->setMaxAltitude($stats->maxAltitude);
71
                $wander->setMinAltitude($stats->minAltitude);
72
                $wander->setCumulativeElevationGain($stats->cumulativeElevationGain);
73
            }
74
            catch(Exception $e) {
75
                //$this->logger->debug("Couldn't set extended GPX property on wander: " . $e->getMessage());
76
                throw new Exception("Couldn't set standard GPX stats properties on wander.", 0, $e);
77
            }
78
        }
79
    }
80
81
    /**
82
     * Update centroid and related angle from "home base" to the centroid,
83
     * using geoPHP. geoPHP is somewhat overkill and annoyingly old-school
84
     * (e.g. in the global namespace) but it's powerful and we may end
85
     * up using it elsewhere.
86
     */
87
    private function updateCentroid(string $gpxxml, Wander $wander): void
88
    {
89
        // Centroid, updated using geoPHP
90
        $gpx = \geoPHP::load($gpxxml, 'gpx'); // It's horrible old code, in the global namespace
91
        $centroid = $gpx->getCentroid();
92
        $wander->setCentroid([$centroid->y(), $centroid->x()]);
93
        $angle = $this->compass((
94
            $centroid->x() - $this->homebaseCoords[1]),
95
            ($centroid->y() - $this->homebaseCoords[0])
96
        );
97
        $wander->setAngleFromHome($angle);
98
    }
99
100
    /**
101
     * Translate co-ordinates relative to 0, 0 into a compass direction in degrees.
102
     */
103
    public function compass(float $x, float $y): float
104
    {
105
        // https://www.php.net/manual/en/function.atan2.php#88119
106
        if($x==0 AND $y==0){ return 0; } // ...or return 360
107
        return ($x < 0)
108
            ? rad2deg(atan2($x,$y)) + 360
109
            : rad2deg(atan2($x,$y));
110
    }
111
112
    public function gpxToGeoJson(string $gpx): string
113
    {
114
        $geometry = \geoPHP::load($gpx, 'gpx');
115
        return $geometry->out('json');
116
    }
117
118
    public function getWanderGeoJson(Wander $wander): string
119
    {
120
        $gpxPath = $this->getFullGpxFilePathFromWander($wander);
121
        $gpxData = file_get_contents($gpxPath);
122
        if ($gpxData === false) {
123
            throw new Exception("Couldn't read GPX data from $gpxPath");
124
        }
125
        return $this->gpxToGeoJson($gpxData);
126
    }
127
128
    // TODO: This is updating more than the stats now. Change the name to
129
    // reflect that.
130
    public function updateWanderFromGpx(Wander $wander): void
131
    {
132
        $filename = $wander->getGpxFilename();
133
        if (isset($filename))
134
        {
135
            // Basic stats, updated using phpGpx
136
            $gpxxml = $this->getGpxStringFromFilename($filename);
137
            $wander->setGeoJson($this->gpxToGeoJson($gpxxml));
138
            $this->updateGeneralStats($gpxxml, $wander);
139
            $this->updateCentroid($gpxxml, $wander);
140
        }
141
    }
142
}