Completed
Push — master ( f12f85...ac41f4 )
by Hannes
02:45
created

AbstractFileProvider::__destruct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Runalyze DEM Reader.
5
 *
6
 * (c) RUNALYZE <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Runalyze\DEM\Provider;
13
14
use Runalyze\DEM\Interpolation\GuessInvalidValuesTrait;
15
use Runalyze\DEM\Interpolation\InterpolationInterface;
16
17
abstract class AbstractFileProvider implements ProviderInterface
18
{
19
    use GuessInvalidValuesTrait;
20
21
    /** @var int */
22
    const MAX_LATITUDE = 90;
23
24
    /** @var int */
25
    const MAX_LONGITUDE = 180;
26
27
    /** @var string */
28
    protected $PathToFiles;
29
30
    /** @var resource */
31
    protected $FileResource;
32
33
    /** @var string|bool */
34
    protected $CurrentFilename = false;
35
36
    /** @var \Runalyze\DEM\Interpolation\InterpolationInterface|null */
37
    protected $Interpolation;
38
39
    /** @var \Runalyze\DEM\Provider\ResourceReaderInterface */
40
    protected $ResourceReader;
41
42
    /**
43
     * @param string                                                  $pathToFiles
44
     * @param \Runalyze\DEM\Interpolation\InterpolationInterface|null $interpolation
45
     */
46 8
    public function __construct($pathToFiles, InterpolationInterface $interpolation = null)
47
    {
48 8
        $this->PathToFiles = $pathToFiles;
49 8
        $this->Interpolation = $interpolation;
50
51 8
        $this->initResourceReader();
52 8
    }
53
54
    /**
55
     * @param \Runalyze\DEM\Interpolation\InterpolationInterface $interpolation
56
     */
57 8
    public function setInterpolation(InterpolationInterface $interpolation)
58
    {
59 8
        $this->Interpolation = $interpolation;
60 8
    }
61
62
    /**
63
     * @param  array $latitudeLongitudes array(array($lat, $lng), ...)
64
     * @return bool
65
     */
66 2
    public function hasDataFor(array $latitudeLongitudes)
67
    {
68 2
        foreach ($latitudeLongitudes as $location) {
69 2
            if (!is_array($location) || count($location) !== 2) {
70
                throw new \InvalidArgumentException('Locations must be arrays of two values.');
71
            }
72
73 2
            if (!$this->locationIsInBounds($location[0], $location[1])) {
74 1
                return false;
75
            }
76
77 1
            if (!file_exists($this->PathToFiles.$this->getFilenameFor($location[0], $location[1]))) {
78
                return false;
79
            }
80 1
        }
81
82 1
        return true;
83
    }
84
85
    /**
86
     * @param  float $latitude
87
     * @param  float $longitude
88
     * @return bool
89
     */
90 8
    protected function locationIsInBounds($latitude, $longitude)
91
    {
92
        return
93 8
            ($latitude > -static::MAX_LATITUDE) && ($latitude <= static::MAX_LATITUDE) &&
94 8
            ($longitude > -static::MAX_LONGITUDE) && ($longitude < static::MAX_LONGITUDE)
95 8
        ;
96
    }
97
98
    /**
99
     * @return bool
100
     */
101 6
    public function usesInterpolation()
102
    {
103 6
        return null !== $this->Interpolation;
104
    }
105
106
    /**
107
     * @param  float[]                   $latitudes
108
     * @param  float[]                   $longitudes
109
     * @throws \InvalidArgumentException
110
     * @return array                     elevations [m] can be false if nothing retrieved
111
     */
112 4
    public function getElevations(array $latitudes, array $longitudes)
113
    {
114 4
        $numberOfPoints = count($latitudes);
115 4
        $elevations = [];
116
117 4
        if (count($longitudes) !== $numberOfPoints) {
118
            throw new \InvalidArgumentException('Arrays for latitude and longitude must be of same size.');
119
        }
120
121 4
        for ($i = 0; $i < $numberOfPoints; ++$i) {
122 4
            $elevations[] = $this->getElevation($latitudes[$i], $longitudes[$i]);
123 4
        }
124
125 4
        return $elevations;
126
    }
127
128
    /**
129
     * Get elevation for specified location.
130
     *
131
     * Availability of data must be checked in advance via hasDataFor($locations).
132
     *
133
     * @param  float    $latitude
134
     * @param  float    $longitude
135
     * @return int|bool elevation [m] can be false if nothing retrieved
136
     */
137 6
    public function getElevation($latitude, $longitude)
138
    {
139 6
        if (!$this->locationIsInBounds($latitude, $longitude)) {
140
            throw new \InvalidArgumentException(
141
                sprintf('Location (%f, %f) is out of bounds ([-%f, %f], [-%f, %f]).',
142
                    $latitude, $longitude,
143
                    static::MAX_LATITUDE, static::MAX_LATITUDE,
144
                    static::MAX_LONGITUDE, static::MAX_LONGITUDE
145
                )
146
            );
147
        }
148
149 6
        $filename = $this->getFilenameFor($latitude, $longitude);
150
151 6
        if ($this->CurrentFilename !== $filename) {
152 6
            $this->openResource($filename);
153 6
        }
154
155 6
        return $this->getElevationFromResource($latitude, $longitude);
156
    }
157
158
    /**
159
     * @param  float    $latitude
160
     * @param  float    $longitude
161
     * @return int|bool can be false if nothing retrieved
162
     */
163 6
    protected function getElevationFromResource($latitude, $longitude)
164
    {
165 6
        list($exactRowValue, $exactColValue) = $this->getExactRowAndColFor($latitude, $longitude);
166
167 6
        if (!$this->usesInterpolation()) {
168
            return $this->getElevationFor(round($exactRowValue), round($exactColValue));
169
        }
170
171 6
        return $this->getInterpolatedElevationFor($exactRowValue, $exactColValue);
172
    }
173
174
    /**
175
     * @param  float    $exactRowValue
176
     * @param  float    $exactColValue
177
     * @return int|bool can be false if nothing retrieved
178
     */
179 6
    protected function getInterpolatedElevationFor($exactRowValue, $exactColValue)
180
    {
181 6
        $row = floor($exactRowValue);
182 6
        $col = floor($exactColValue);
183
184
        $elevationOnBoundingBox = [
185 6
            $this->getElevationFor($row, $col),
186 6
            $this->getElevationFor($row, $col + 1),
187 6
            $this->getElevationFor($row + 1, $col),
188 6
            $this->getElevationFor($row + 1, $col + 1),
189 6
        ];
190
191 6
        $this->guessInvalidValuesOnBox($elevationOnBoundingBox);
192
193 6
        return $this->Interpolation->interpolate(
194 6
            ($exactRowValue - $row),
195 6
            ($exactColValue - $col),
196
            $elevationOnBoundingBox
197 6
        );
198
    }
199
200
    /**
201
     * @param  string $filename
202
     * @return bool
203
     */
204
    protected function resourceIsCurrentlyOpened($filename)
205
    {
206
        return $filename !== $this->CurrentFilename;
207
    }
208
209
    /**
210
     * @param  float  $latitude
211
     * @param  float  $longitude
212
     * @return string
213
     */
214
    abstract protected function getFilenameFor($latitude, $longitude);
215
216
    abstract protected function initResourceReader();
217
218
    /**
219
     * @param string $filename
220
     */
221
    abstract protected function openResource($filename);
222
223
    /**
224
     * @param  int      $row
225
     * @param  int      $col
226
     * @return int|bool elevation [m] can be false if nothing retrieved
227
     */
228
    abstract protected function getElevationFor($row, $col);
229
230
    /**
231
     * @param  float $latitude
232
     * @param  float $longitude
233
     * @return array array(row, col)
234
     */
235
    abstract protected function getExactRowAndColFor($latitude, $longitude);
236
}
237