| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- <?php
- /**
- * User: ssh
- * Date: 2019/11/20
- * Time: 23:21
- */
- namespace common\components;
- use linslin\yii2\curl;
- use yii\helpers\Json;
- class mapUtil
- {
- public static $thirdMapKey = 'd4ab169335937fb56581ba15ea1bd1a1';
- public static function suggestion($region, $keyword)
- {
- if (getenv('YII_ENV') == 'production') {
- $url = "https://restapi.amap.com/v3/assistant/inputtips?city={$region}&keywords={$keyword}&key=" . self::$thirdMapKey;
- } else {
- $url = "http://restapi.amap.com/v3/assistant/inputtips?city={$region}&keywords={$keyword}&key=" . self::$thirdMapKey;
- }
- $curl = new curl\Curl();
- $result = $curl->get($url);
- return Json::decode($result);
- }
- //计算距离 lng经度 lat纬度 ssh 2020.5.14
- public static function calculateDistance($fromLnt, $fromLat, $toLnt, $toLat)
- {
- if (getenv('YII_ENV') == 'production') {
- $url = "https://restapi.amap.com/v5/direction/electrobike?key=" . self::$thirdMapKey . '&origin=' . $fromLnt . ',' . $fromLat . '&destination=' . $toLnt . ',' . $toLat;
- } else {
- $url = "http://restapi.amap.com/v5/direction/electrobike?key=" . self::$thirdMapKey . '&origin=' . $fromLnt . ',' . $fromLat . '&destination=' . $toLnt . ',' . $toLat;
- }
- $curl = new curl\Curl();
- $result = $curl->get($url);
- $result = Json::decode($result);
- $distance = 0;
- if (!empty($result['status']) && $result['status'] == 1) {
- $path = $result['route']['paths'] ?? [];
- $current = $path[0] ?? [];
- $distance = $current['distance'] ?? 0;
- }
- return $distance;
- }
- //计算客户到店距离,骑电动车 ssh 2026.7.30
- public static function calcShopDistanceByEleBike($beginPlace, $toPlace, $report = 1)
- {
- $beginLnt = $beginPlace->long ?? '';
- $beginLat = $beginPlace->lat ?? '';
- $toLnt = $toPlace->long ?? '';
- $toLat = $toPlace->lat ?? '';
- // 校验经纬度参数格式
- if (empty($beginLnt) || empty($beginLat)) {
- if ($report == 1) {
- util::fail('计算距离失败,请完善地址');
- }
- return ['distance' => 0, 'distanceKm' => 0];
- }
- if (!is_numeric($beginLnt) || !is_numeric($beginLat)) {
- if ($report == 1) {
- util::fail('计算距离失败,请完善地址');
- }
- return ['distance' => 0, 'distanceKm' => 0];
- }
- if (empty($toLnt) || empty($toLat)) {
- if ($report == 1) {
- util::fail('计算距离失败,商家地址没有完善');
- }
- return ['distance' => 0, 'distanceKm' => 0];
- }
- if (!is_numeric($toLnt) || !is_numeric($toLat)) {
- if ($report == 1) {
- util::fail('计算距离失败,商家地址没有完善');
- }
- return ['distance' => 0, 'distanceKm' => 0];
- }
- $distance = self::calculateDistance($beginLnt, $beginLat, $toLnt, $toLat);
- $distanceKm = bcdiv($distance, 1000, 2);
- return ['distance' => $distance, 'distanceKm' => $distanceKm];
- }
- /**
- * 批量计算多个起点到一个终点的距离 (高德 V3 距离测量接口)
- * @param array $origins 起点坐标数组,例如:[['lng' => '116.481028', 'lat' => '39.989643'], ...]
- * @param string $toLnt 终点经度
- * @param string $toLat 终点纬度
- * @param int $type 路径计算的方式和方法 (0:直线距离, 1:驾车, 3:步行)
- * @return array
- */
- public static function calculateBatchDistance($origins, $toLnt, $toLat, $type = 1)
- {
- if (empty($origins)) {
- return [];
- }
- // 将起点数组拼接成高德要求的格式: lng1,lat1|lng2,lat2
- $originStrs = [];
- foreach ($origins as $origin) {
- $originStrs[] = $origin['lng'] . ',' . $origin['lat'];
- }
- $originsParam = implode('|', $originStrs);
- $destinationParam = $toLnt . ',' . $toLat;
- $domain = getenv('YII_ENV') == 'production' ? 'https://restapi.amap.com' : 'http://restapi.amap.com';
- $url = $domain . "/v3/distance?key=" . self::$thirdMapKey . "&origins={$originsParam}&destination={$destinationParam}&type={$type}";
- $curl = new curl\Curl();
- $result = $curl->get($url);
- $result = \yii\helpers\Json::decode($result);
- $distances = [];
- if (!empty($result['status']) && $result['status'] == 1 && !empty($result['results'])) {
- foreach ($result['results'] as $index => $res) {
- // 返回的结果顺序与传入的 origins 顺序一致
- $distances[$index] = $res['distance'] ?? 0;
- }
- } else {
- noticeUtil::push("高德批量测距接口返回失败:" . json_encode($result) . " url:" . $url);
- }
- return $distances;
- }
- /**
- * 逆地址解析(高德),根据经纬度解析省市区与地址
- * @param string|float $lat 纬度
- * @param string|float $long 经度
- * @param string $locationType gcj02|wgs84,wgs84 时传 coordsys=gps 给高德
- * @return array
- */
- public static function resolveLocation($lat, $long, $locationType = 'gcj02')
- {
- $lat = (float)$lat;
- $long = (float)$long;
- if ($lat == 0 || $long == 0) {
- return [];
- }
- $domain = getenv('YII_ENV') == 'production' ? 'https://restapi.amap.com' : 'http://restapi.amap.com';
- // 高德 location 参数:经度,纬度
- $location = $long . ',' . $lat;
- $url = $domain . '/v3/geocode/regeo?location=' . urlencode($location)
- . '&key=' . self::$thirdMapKey
- . '&extensions=base';
- if ($locationType === 'wgs84' || $locationType === 'gps') {
- $url .= '&coordsys=gps';
- }
- $curl = new curl\Curl();
- $result = $curl->get($url);
- $result = Json::decode($result);
- return self::formatAmapRegeoResult($result);
- }
- /**
- * 格式化高德逆地理编码结果
- */
- private static function formatAmapRegeoResult($result)
- {
- if (empty($result['status']) || (string)$result['status'] !== '1' || empty($result['regeocode'])) {
- return [];
- }
- $regeo = $result['regeocode'];
- $component = $regeo['addressComponent'] ?? [];
- $province = self::normalizeAmapRegionValue($component['province'] ?? '');
- $city = self::normalizeAmapRegionValue($component['city'] ?? '');
- $district = self::normalizeAmapRegionValue($component['district'] ?? '');
- if (empty($city)) {
- $city = $province;
- }
- $showAddress = $regeo['formatted_address'] ?? '';
- $township = self::normalizeAmapRegionValue($component['township'] ?? '');
- $streetNumber = $component['streetNumber'] ?? [];
- $street = is_array($streetNumber) ? ($streetNumber['street'] ?? '') : '';
- $number = is_array($streetNumber) ? ($streetNumber['number'] ?? '') : '';
- $address = trim($township . $street . $number);
- if (empty($address)) {
- $address = $showAddress;
- if ($province && mb_strpos($address, $province) === 0) {
- $address = mb_substr($address, mb_strlen($province));
- }
- if ($city && $city !== $province && mb_strpos($address, $city) === 0) {
- $address = mb_substr($address, mb_strlen($city));
- }
- if ($district && mb_strpos($address, $district) === 0) {
- $address = mb_substr($address, mb_strlen($district));
- }
- $address = trim($address);
- }
- if (empty($address) || empty($province) || empty($city)) {
- return [];
- }
- return [
- 'province' => $province,
- 'city' => $city,
- 'district' => $district,
- 'address' => $address,
- 'showAddress' => $showAddress ?: ($province . $city . $district . $address),
- ];
- }
- /**
- * 根据经纬度获取当前位置及附近 POI 列表(高德逆地理 extensions=all)
- */
- public static function resolveNearby($lat, $long, $locationType = 'gcj02')
- {
- $lat = (float)$lat;
- $long = (float)$long;
- if ($lat == 0 || $long == 0) {
- return [];
- }
- $domain = getenv('YII_ENV') == 'production' ? 'https://restapi.amap.com' : 'http://restapi.amap.com';
- $location = $long . ',' . $lat;
- $url = $domain . '/v3/geocode/regeo?location=' . urlencode($location)
- . '&key=' . self::$thirdMapKey
- . '&extensions=all&radius=1000&roadlevel=0';
- if ($locationType === 'wgs84' || $locationType === 'gps') {
- $url .= '&coordsys=gps';
- }
- $curl = new curl\Curl();
- $result = Json::decode($curl->get($url));
- if (empty($result['status']) || (string)$result['status'] !== '1' || empty($result['regeocode'])) {
- return [];
- }
- $regeo = $result['regeocode'];
- $component = $regeo['addressComponent'] ?? [];
- $province = self::normalizeAmapRegionValue($component['province'] ?? '');
- $city = self::normalizeAmapRegionValue($component['city'] ?? '');
- $district = self::normalizeAmapRegionValue($component['district'] ?? '');
- if (empty($city)) {
- $city = $province;
- }
- $centerAddress = $regeo['formatted_address'] ?? '';
- $centerName = self::normalizeAmapRegionValue($component['township'] ?? '');
- if (empty($centerName)) {
- $centerName = '当前位置';
- }
- $list = [[
- 'name' => $centerName,
- 'address' => $centerAddress,
- 'province' => $province,
- 'city' => $city,
- 'district' => $district,
- 'location' => $long . ',' . $lat,
- 'showAddress' => $centerAddress,
- ]];
- $pois = $regeo['pois'] ?? [];
- $exists = [$centerName];
- foreach ($pois as $poi) {
- $poiName = $poi['name'] ?? '';
- if ($poiName === '' || in_array($poiName, $exists, true)) {
- continue;
- }
- $exists[] = $poiName;
- $poiAddress = $poi['address'] ?? '';
- if ($poiAddress === '') {
- $poiAddress = $centerAddress ? ($centerAddress . ' ' . $poiName) : $poiName;
- }
- $list[] = [
- 'name' => $poiName,
- 'address' => $poiAddress,
- 'province' => $province,
- 'city' => $city,
- 'district' => $district,
- 'location' => $poi['location'] ?? ($long . ',' . $lat),
- 'showAddress' => $poiAddress,
- ];
- if (count($list) >= 21) {
- break;
- }
- }
- return $list;
- }
- private static function normalizeAmapRegionValue($value)
- {
- if (is_array($value)) {
- return !empty($value) ? (string)$value[0] : '';
- }
- return (string)$value;
- }
- /**
- * 坐标系转换:从 WGS84 转换为 GCJ-02
- * @param $lng
- * @param $lat
- * @return array
- */
- public static function wgs84ToGcj02($lng, $lat)
- {
- $lng = (float)$lng;
- $lat = (float)$lat;
- if (self::outOfChina($lng, $lat)) {
- return ['lng' => $lng, 'lat' => $lat];
- }
- $a = 6378245.0;
- $ee = 0.00669342162296594323;
- $dLat = self::transformLat($lng - 105.0, $lat - 35.0);
- $dLng = self::transformLng($lng - 105.0, $lat - 35.0);
- $radLat = $lat / 180.0 * M_PI;
- $magic = sin($radLat);
- $magic = 1 - $ee * $magic * $magic;
- $sqrtMagic = sqrt($magic);
- $dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * M_PI);
- $dLng = ($dLng * 180.0) / ($a / $sqrtMagic * cos($radLat) * M_PI);
- return ['lng' => $lng + $dLng, 'lat' => $lat + $dLat];
- }
- private static function transformLat($lng, $lat)
- {
- $ret = -100.0 + 2.0 * $lng + 3.0 * $lat + 0.2 * $lat * $lat + 0.1 * $lng * $lat + 0.2 * sqrt(abs($lng));
- $ret += (20.0 * sin(6.0 * $lng * M_PI) + 20.0 * sin(2.0 * $lng * M_PI)) * 2.0 / 3.0;
- $ret += (20.0 * sin($lat * M_PI) + 40.0 * sin($lat / 3.0 * M_PI)) * 2.0 / 3.0;
- $ret += (160.0 * sin($lat / 12.0 * M_PI) + 320.0 * sin($lat * M_PI / 30.0)) * 2.0 / 3.0;
- return $ret;
- }
- private static function transformLng($lng, $lat)
- {
- $ret = 300.0 + $lng + 2.0 * $lat + 0.1 * $lng * $lng + 0.1 * $lng * $lat + 0.1 * sqrt(abs($lng));
- $ret += (20.0 * sin(6.0 * $lng * M_PI) + 20.0 * sin(2.0 * $lng * M_PI)) * 2.0 / 3.0;
- $ret += (20.0 * sin($lng * M_PI) + 40.0 * sin($lng / 3.0 * M_PI)) * 2.0 / 3.0;
- $ret += (150.0 * sin($lng / 12.0 * M_PI) + 300.0 * sin($lng / 30.0 * M_PI)) * 2.0 / 3.0;
- return $ret;
- }
- private static function outOfChina($lng, $lat)
- {
- if ($lng < 72.004 || $lng > 137.8347) {
- return true;
- }
- if ($lat < 0.8293 || $lat > 55.8271) {
- return true;
- }
- return false;
- }
- }
|