Geohash::decode()   C
last analyzed

Complexity

Conditions 12
Paths 24

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 12

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 56
ccs 31
cts 31
cp 1
rs 6.5333
cc 12
nc 24
nop 1
crap 12

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Geotools library.
5
 *
6
 * (c) Antoine Corcy <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace League\Geotools\Geohash;
13
14
use League\Geotools\Coordinate\Coordinate;
15
use League\Geotools\Coordinate\CoordinateInterface;
16
use League\Geotools\Exception\InvalidArgumentException;
17
use League\Geotools\Exception\RuntimeException;
18
19
/**
20
 * Geohash class
21
 *
22
 * @author Antoine Corcy <[email protected]>
23
 */
24
class Geohash implements GeohashInterface
25
{
26
    /**
27
     * The minimum length of the geo hash.
28
     *
29
     * @var integer
30
     */
31
    const MIN_LENGTH = 1;
32
33
    /**
34
     * The maximum length of the geo hash.
35
     *
36
     * @var integer
37
     */
38
    const MAX_LENGTH = 12;
39
40
41
    /**
42
     * The geo hash.
43
     *
44
     * @var string
45
     */
46
    protected $geohash;
47
48
    /**
49
     * The interval of latitudes in degrees.
50
     *
51
     * @var array
52
     */
53
    protected $latitudeInterval = array(-90.0, 90.0);
54
55
    /**
56
     * The interval of longitudes in degrees.
57
     *
58
     * @var array
59
     */
60
    protected $longitudeInterval = array(-180.0, 180.0);
61
62
    /**
63
     * The interval of bits.
64
     *
65
     * @var array
66
     */
67
    protected $bits = array(16, 8, 4, 2, 1);
68
69
    /**
70
     * The array of chars in base 32.
71
     *
72
     * @var array
73
     */
74
    protected $base32Chars = array(
75
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g',
76
        'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
77
    );
78
79
80
    /**
81
     * Returns the geo hash.
82
     *
83
     * @return string
84
     */
85 5
    public function getGeohash()
86
    {
87 5
        return $this->geohash;
88
    }
89
90
    /**
91
     * Returns the decoded coordinate (The center of the bounding box).
92
     *
93
     * @return CoordinateInterface
94
     */
95 5
    public function getCoordinate()
96
    {
97 5
        return new Coordinate(array(
98 5
            ($this->latitudeInterval[0] + $this->latitudeInterval[1]) / 2,
99 5
            ($this->longitudeInterval[0] + $this->longitudeInterval[1]) / 2
100
        ));
101
    }
102
103
    /**
104
     * Returns the bounding box which is an array of coordinates (SouthWest & NorthEast).
105
     *
106
     * @return CoordinateInterface[]
107
     */
108 6
    public function getBoundingBox()
109
    {
110
        return array(
111 6
            new Coordinate(array(
112 6
                $this->latitudeInterval[0],
113 6
                $this->longitudeInterval[0]
114
            )),
115 6
            new Coordinate(array(
116 6
                $this->latitudeInterval[1],
117 6
                $this->longitudeInterval[1]
118
            ))
119
        );
120
    }
121
122
    /**
123
     * {@inheritDoc}
124
     *
125
     * @see http://en.wikipedia.org/wiki/Geohash
126
     * @see http://geohash.org/
127
     */
128 19
    public function encode(CoordinateInterface $coordinate, $length = self::MAX_LENGTH)
129
    {
130 19
        if ((int) $length < self::MIN_LENGTH || (int) $length > self::MAX_LENGTH) {
131 10
            throw new InvalidArgumentException('The length should be between 1 and 12.');
132
        }
133
134 9
        $latitudeInterval  = $this->latitudeInterval;
135 9
        $longitudeInterval = $this->longitudeInterval;
136 9
        $isEven            = true;
137 9
        $bit               = 0;
138 9
        $charIndex         = 0;
139
140 9
        while (strlen($this->geohash) < $length) {
141 9
            if ($isEven) {
142 9
                $middle = ($longitudeInterval[0] + $longitudeInterval[1]) / 2;
143 9
                if ($coordinate->getLongitude() > $middle) {
144 8
                    $charIndex |= $this->bits[$bit];
145 8
                    $longitudeInterval[0] = $middle;
146
                } else {
147 9
                    $longitudeInterval[1] = $middle;
148
                }
149
            } else {
150 9
                $middle = ($latitudeInterval[0] + $latitudeInterval[1]) / 2;
151 9
                if ($coordinate->getLatitude() > $middle) {
152 8
                    $charIndex |= $this->bits[$bit];
153 8
                    $latitudeInterval[0] = $middle;
154
                } else {
155 9
                    $latitudeInterval[1] = $middle;
156
                }
157
            }
158
159 9
            if ($bit < 4) {
160 9
                $bit++;
161
            } else {
162 9
                $this->geohash = $this->geohash . $this->base32Chars[$charIndex];
163 9
                $bit           = 0;
164 9
                $charIndex     = 0;
165
            }
166
167 9
            $isEven = $isEven ? false : true;
168
        }
169
170 9
        $this->latitudeInterval  = $latitudeInterval;
171 9
        $this->longitudeInterval = $longitudeInterval;
172
173 9
        return $this;
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179 25
    public function decode($geohash)
180
    {
181 25
        if (!is_string($geohash)) {
182 4
            throw new InvalidArgumentException('The geo hash should be a string.');
183
        }
184
185 21
        if (strlen($geohash) < self::MIN_LENGTH || strlen($geohash) > self::MAX_LENGTH) {
186 4
            throw new InvalidArgumentException('The length of the geo hash should be between 1 and 12.');
187
        }
188
189 17
        $base32DecodeMap  = array();
190 17
        $base32CharsTotal = count($this->base32Chars);
191 17
        for ($i = 0; $i < $base32CharsTotal; $i++) {
192 17
            $base32DecodeMap[$this->base32Chars[$i]] = $i;
193
        }
194
195 17
        $latitudeInterval  = $this->latitudeInterval;
196 17
        $longitudeInterval = $this->longitudeInterval;
197 17
        $isEven            = true;
198
199 17
        $geohashLength = strlen($geohash);
200 17
        for ($i = 0; $i < $geohashLength; $i++) {
201
202 17
            if (!isset($base32DecodeMap[$geohash[$i]])) {
203 8
                throw new RuntimeException('This geo hash is invalid.');
204
            }
205
206 10
            $currentChar = $base32DecodeMap[$geohash[$i]];
207
208 10
            $bitsTotal = count($this->bits);
209 10
            for ($j = 0; $j < $bitsTotal; $j++) {
210 10
                $mask = $this->bits[$j];
211
212 10
                if ($isEven) {
213 10
                    if (($currentChar & $mask) !== 0) {
214 10
                        $longitudeInterval[0] = ($longitudeInterval[0] + $longitudeInterval[1]) / 2;
215
                    } else {
216 10
                        $longitudeInterval[1] = ($longitudeInterval[0] + $longitudeInterval[1]) / 2;
217
                    }
218
                } else {
219 10
                    if (($currentChar & $mask) !== 0) {
220 10
                        $latitudeInterval[0] = ($latitudeInterval[0] + $latitudeInterval[1]) / 2;
221
                    } else {
222 9
                        $latitudeInterval[1] = ($latitudeInterval[0] + $latitudeInterval[1]) / 2;
223
                    }
224
                }
225
226 10
                $isEven = $isEven ? false : true;
227
            }
228
        }
229
230 9
        $this->latitudeInterval  = $latitudeInterval;
231 9
        $this->longitudeInterval = $longitudeInterval;
232
233 9
        return $this;
234
    }
235
}
236