1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Cobak78\GeoHash; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Class GeoHash |
7
|
|
|
* @package Cobak78\GeoHash |
8
|
|
|
*/ |
9
|
|
|
class GeoHash |
10
|
|
|
{ |
11
|
|
|
/** |
12
|
|
|
* @param float $lat1 |
13
|
|
|
* @param float $lon1 |
14
|
|
|
* @param float $lat2 |
15
|
|
|
* @param float $lon2 |
16
|
|
|
* @param string $unit |
17
|
|
|
* @return float |
18
|
|
|
*/ |
19
|
2 |
|
public function distance( |
20
|
|
|
float $lat1, |
21
|
|
|
float $lon1, |
22
|
|
|
float $lat2, |
23
|
|
|
float $lon2, |
24
|
|
|
string $unit = 'K' |
25
|
|
|
) { |
26
|
2 |
|
$theta = $lon1 - $lon2; |
27
|
2 |
|
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); |
28
|
2 |
|
$dist = acos($dist); |
29
|
2 |
|
$dist = rad2deg($dist); |
30
|
2 |
|
$miles = $dist * 60 * 1.1515; |
31
|
|
|
|
32
|
2 |
|
switch (strtoupper($unit)) { |
33
|
2 |
|
case "K": |
34
|
1 |
|
case "KM": |
35
|
2 |
|
return ($miles * 1.609344); |
36
|
1 |
|
case "MN": |
37
|
1 |
|
case "N": |
38
|
1 |
|
return ($miles * 0.8684); |
39
|
|
|
default: |
40
|
1 |
|
return $miles; |
41
|
|
|
} |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* geoBoundBox: [ top_left => [ lat => x, lon => y ], bottom_right => [ lat => x, lon => y ] |
46
|
|
|
* |
47
|
|
|
* @param array $geoBoundBox |
48
|
|
|
* @param int $squares |
49
|
|
|
* @return mixed |
50
|
|
|
* @throws \Exception |
51
|
|
|
*/ |
52
|
1 |
|
public function getGeoHashPrecision(array $geoBoundBox, int $squares) |
53
|
|
|
{ |
54
|
1 |
|
$remainder = $squares % 2; |
55
|
1 |
|
$xQuotient = $squares / 2; |
56
|
1 |
|
$yQuotient = $xQuotient / 2; |
57
|
|
|
|
58
|
1 |
|
if (0 !== $remainder) { |
59
|
|
|
throw new \InvalidArgumentException('geoHash divisions must be multiple of 2'); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
// get x distance |
63
|
1 |
|
$xDist = $this->distance($geoBoundBox['top_left']['lat'], $geoBoundBox['top_left']['lon'], $geoBoundBox['bottom_right']['lat'], $geoBoundBox['top_left']['lon']); |
64
|
1 |
|
$xDist = $xDist * 1000; |
65
|
|
|
// get y distance |
66
|
1 |
|
$yDist = $this->distance($geoBoundBox['top_left']['lat'], $geoBoundBox['top_left']['lon'], $geoBoundBox['top_left']['lat'], $geoBoundBox['bottom_right']['lon']); |
67
|
1 |
|
$yDist = $yDist * 1000; |
68
|
|
|
|
69
|
|
|
// geohash distances |
70
|
1 |
|
$xGeohashDist = $xDist / $xQuotient; |
71
|
1 |
|
$yGeohashDist = $yDist / $yQuotient; |
72
|
|
|
|
73
|
1 |
|
return $this->getPrecisionFromArea($xGeohashDist, $yGeohashDist); |
74
|
|
|
|
75
|
|
|
} |
76
|
|
|
|
77
|
1 |
|
private function getPrecisionFromArea($width, $height) |
78
|
|
|
{ |
79
|
|
|
// return |
80
|
|
|
$precisionMap = [ |
81
|
1 |
|
1 => [5009400, 4992000.6], |
82
|
|
|
2 => [1252300, 624100], |
83
|
|
|
3 => [156500, 156000], |
84
|
|
|
4 => [39100, 19500], |
85
|
|
|
5 => [4900, 4900], |
86
|
|
|
6 => [1200, 609.4], |
87
|
|
|
7 => [152.9, 152.4], |
88
|
|
|
8 => [38.2, 19], |
89
|
|
|
9 => [4.8, 4.8], |
90
|
|
|
10 => [1.2, 0.595], |
91
|
|
|
11 => [0.14, 0.149], |
92
|
|
|
12 => [0.037, 0.019] |
93
|
|
|
]; |
94
|
|
|
|
95
|
1 |
|
foreach ($precisionMap as $key => $value) { |
96
|
1 |
|
if ($width > $value[0] || $height > $value[1]) { |
97
|
1 |
|
return $key; |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
return 12; |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|