Completed
Push — master ( 59abba...d75f37 )
by Hannes
06:25
created

AbstractFileProvider::getExactRowAndColFor()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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