Completed
Push — master ( 7a5cb9...59abba )
by Hannes
02:54
created

AbstractFileProvider::removeInterpolation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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