1 | <?php |
||
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() |
|
89 | |||
90 | /** |
||
91 | * Returns the decoded coordinate (The center of the bounding box). |
||
92 | * |
||
93 | * @return CoordinateInterface |
||
94 | */ |
||
95 | 5 | public function getCoordinate() |
|
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() |
|
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) |
|
235 | } |
||
236 |