mapUtil.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. /**
  3. * User: ssh
  4. * Date: 2019/11/20
  5. * Time: 23:21
  6. */
  7. namespace common\components;
  8. use linslin\yii2\curl;
  9. use yii\helpers\Json;
  10. class mapUtil
  11. {
  12. public static $thirdMapKey = 'd4ab169335937fb56581ba15ea1bd1a1';
  13. public static function suggestion($region, $keyword)
  14. {
  15. if (getenv('YII_ENV') == 'production') {
  16. $url = "https://restapi.amap.com/v3/assistant/inputtips?city={$region}&keywords={$keyword}&key=" . self::$thirdMapKey;
  17. } else {
  18. $url = "http://restapi.amap.com/v3/assistant/inputtips?city={$region}&keywords={$keyword}&key=" . self::$thirdMapKey;
  19. }
  20. $curl = new curl\Curl();
  21. $result = $curl->get($url);
  22. return Json::decode($result);
  23. }
  24. //计算距离 lng经度 lat纬度 ssh 2020.5.14
  25. public static function calculateDistance($fromLnt, $fromLat, $toLnt, $toLat)
  26. {
  27. if (getenv('YII_ENV') == 'production') {
  28. $url = "https://restapi.amap.com/v5/direction/electrobike?key=" . self::$thirdMapKey . '&origin=' . $fromLnt . ',' . $fromLat . '&destination=' . $toLnt . ',' . $toLat;
  29. } else {
  30. $url = "http://restapi.amap.com/v5/direction/electrobike?key=" . self::$thirdMapKey . '&origin=' . $fromLnt . ',' . $fromLat . '&destination=' . $toLnt . ',' . $toLat;
  31. }
  32. $curl = new curl\Curl();
  33. $result = $curl->get($url);
  34. $result = Json::decode($result);
  35. $distance = 0;
  36. if (!empty($result['status']) && $result['status'] == 1) {
  37. $path = $result['route']['paths'] ?? [];
  38. $current = $path[0] ?? [];
  39. $distance = $current['distance'] ?? 0;
  40. }
  41. return $distance;
  42. }
  43. /**
  44. * 批量计算多个起点到一个终点的距离 (高德 V3 距离测量接口)
  45. * @param array $origins 起点坐标数组,例如:[['lng' => '116.481028', 'lat' => '39.989643'], ...]
  46. * @param string $toLnt 终点经度
  47. * @param string $toLat 终点纬度
  48. * @param int $type 路径计算的方式和方法 (0:直线距离, 1:驾车, 3:步行)
  49. * @return array
  50. */
  51. public static function calculateBatchDistance($origins, $toLnt, $toLat, $type = 1)
  52. {
  53. if (empty($origins)) {
  54. return [];
  55. }
  56. // 将起点数组拼接成高德要求的格式: lng1,lat1|lng2,lat2
  57. $originStrs = [];
  58. foreach ($origins as $origin) {
  59. $originStrs[] = $origin['lng'] . ',' . $origin['lat'];
  60. }
  61. $originsParam = implode('|', $originStrs);
  62. $destinationParam = $toLnt . ',' . $toLat;
  63. $domain = getenv('YII_ENV') == 'production' ? 'https://restapi.amap.com' : 'http://restapi.amap.com';
  64. $url = $domain . "/v3/distance?key=" . self::$thirdMapKey . "&origins={$originsParam}&destination={$destinationParam}&type={$type}";
  65. $curl = new curl\Curl();
  66. $result = $curl->get($url);
  67. $result = \yii\helpers\Json::decode($result);
  68. $distances = [];
  69. if (!empty($result['status']) && $result['status'] == 1 && !empty($result['results'])) {
  70. foreach ($result['results'] as $index => $res) {
  71. // 返回的结果顺序与传入的 origins 顺序一致
  72. $distances[$index] = $res['distance'] ?? 0;
  73. }
  74. }else{
  75. noticeUtil::push("高德批量测距接口返回失败:".json_encode($result)." url:".$url);
  76. }
  77. return $distances;
  78. }
  79. /**
  80. * 逆地址解析(高德),根据经纬度解析省市区与地址
  81. * @param string|float $lat 纬度
  82. * @param string|float $long 经度
  83. * @param string $locationType gcj02|wgs84,wgs84 时传 coordsys=gps 给高德
  84. * @return array
  85. */
  86. public static function resolveLocation($lat, $long, $locationType = 'gcj02')
  87. {
  88. $lat = (float)$lat;
  89. $long = (float)$long;
  90. if ($lat == 0 || $long == 0) {
  91. return [];
  92. }
  93. $domain = getenv('YII_ENV') == 'production' ? 'https://restapi.amap.com' : 'http://restapi.amap.com';
  94. // 高德 location 参数:经度,纬度
  95. $location = $long . ',' . $lat;
  96. $url = $domain . '/v3/geocode/regeo?location=' . urlencode($location)
  97. . '&key=' . self::$thirdMapKey
  98. . '&extensions=base';
  99. if ($locationType === 'wgs84' || $locationType === 'gps') {
  100. $url .= '&coordsys=gps';
  101. }
  102. $curl = new curl\Curl();
  103. $result = $curl->get($url);
  104. $result = Json::decode($result);
  105. return self::formatAmapRegeoResult($result);
  106. }
  107. /**
  108. * 格式化高德逆地理编码结果
  109. */
  110. private static function formatAmapRegeoResult($result)
  111. {
  112. if (empty($result['status']) || (string)$result['status'] !== '1' || empty($result['regeocode'])) {
  113. return [];
  114. }
  115. $regeo = $result['regeocode'];
  116. $component = $regeo['addressComponent'] ?? [];
  117. $province = self::normalizeAmapRegionValue($component['province'] ?? '');
  118. $city = self::normalizeAmapRegionValue($component['city'] ?? '');
  119. $district = self::normalizeAmapRegionValue($component['district'] ?? '');
  120. if (empty($city)) {
  121. $city = $province;
  122. }
  123. $showAddress = $regeo['formatted_address'] ?? '';
  124. $township = self::normalizeAmapRegionValue($component['township'] ?? '');
  125. $streetNumber = $component['streetNumber'] ?? [];
  126. $street = is_array($streetNumber) ? ($streetNumber['street'] ?? '') : '';
  127. $number = is_array($streetNumber) ? ($streetNumber['number'] ?? '') : '';
  128. $address = trim($township . $street . $number);
  129. if (empty($address)) {
  130. $address = $showAddress;
  131. if ($province && mb_strpos($address, $province) === 0) {
  132. $address = mb_substr($address, mb_strlen($province));
  133. }
  134. if ($city && $city !== $province && mb_strpos($address, $city) === 0) {
  135. $address = mb_substr($address, mb_strlen($city));
  136. }
  137. if ($district && mb_strpos($address, $district) === 0) {
  138. $address = mb_substr($address, mb_strlen($district));
  139. }
  140. $address = trim($address);
  141. }
  142. if (empty($address) || empty($province) || empty($city)) {
  143. return [];
  144. }
  145. return [
  146. 'province' => $province,
  147. 'city' => $city,
  148. 'district' => $district,
  149. 'address' => $address,
  150. 'showAddress' => $showAddress ?: ($province . $city . $district . $address),
  151. ];
  152. }
  153. private static function normalizeAmapRegionValue($value)
  154. {
  155. if (is_array($value)) {
  156. return !empty($value) ? (string)$value[0] : '';
  157. }
  158. return (string)$value;
  159. }
  160. /**
  161. * 坐标系转换:从 WGS84 转换为 GCJ-02
  162. * @param $lng
  163. * @param $lat
  164. * @return array
  165. */
  166. public static function wgs84ToGcj02($lng, $lat)
  167. {
  168. $lng = (float)$lng;
  169. $lat = (float)$lat;
  170. if (self::outOfChina($lng, $lat)) {
  171. return ['lng' => $lng, 'lat' => $lat];
  172. }
  173. $a = 6378245.0;
  174. $ee = 0.00669342162296594323;
  175. $dLat = self::transformLat($lng - 105.0, $lat - 35.0);
  176. $dLng = self::transformLng($lng - 105.0, $lat - 35.0);
  177. $radLat = $lat / 180.0 * M_PI;
  178. $magic = sin($radLat);
  179. $magic = 1 - $ee * $magic * $magic;
  180. $sqrtMagic = sqrt($magic);
  181. $dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * M_PI);
  182. $dLng = ($dLng * 180.0) / ($a / $sqrtMagic * cos($radLat) * M_PI);
  183. return ['lng' => $lng + $dLng, 'lat' => $lat + $dLat];
  184. }
  185. private static function transformLat($lng, $lat)
  186. {
  187. $ret = -100.0 + 2.0 * $lng + 3.0 * $lat + 0.2 * $lat * $lat + 0.1 * $lng * $lat + 0.2 * sqrt(abs($lng));
  188. $ret += (20.0 * sin(6.0 * $lng * M_PI) + 20.0 * sin(2.0 * $lng * M_PI)) * 2.0 / 3.0;
  189. $ret += (20.0 * sin($lat * M_PI) + 40.0 * sin($lat / 3.0 * M_PI)) * 2.0 / 3.0;
  190. $ret += (160.0 * sin($lat / 12.0 * M_PI) + 320.0 * sin($lat * M_PI / 30.0)) * 2.0 / 3.0;
  191. return $ret;
  192. }
  193. private static function transformLng($lng, $lat)
  194. {
  195. $ret = 300.0 + $lng + 2.0 * $lat + 0.1 * $lng * $lng + 0.1 * $lng * $lat + 0.1 * sqrt(abs($lng));
  196. $ret += (20.0 * sin(6.0 * $lng * M_PI) + 20.0 * sin(2.0 * $lng * M_PI)) * 2.0 / 3.0;
  197. $ret += (20.0 * sin($lng * M_PI) + 40.0 * sin($lng / 3.0 * M_PI)) * 2.0 / 3.0;
  198. $ret += (150.0 * sin($lng / 12.0 * M_PI) + 300.0 * sin($lng / 30.0 * M_PI)) * 2.0 / 3.0;
  199. return $ret;
  200. }
  201. private static function outOfChina($lng, $lat)
  202. {
  203. if ($lng < 72.004 || $lng > 137.8347) {
  204. return true;
  205. }
  206. if ($lat < 0.8293 || $lat > 55.8271) {
  207. return true;
  208. }
  209. return false;
  210. }
  211. }