$area['province'] . '-' . $area['city'] . '-' . $area['district'], 'address' => $address, 'formatted_address' => $province . $city . $area['district'] . $address, ]; } protected static function key() { $key = function_exists('config') ? config('tianditu.key') : ''; if (!$key) { $file = dirname(__DIR__, 2) . '/extra/tianditu.php'; $config = is_file($file) ? include $file : []; $key = is_array($config) && !empty($config['key']) ? $config['key'] : ''; } return trim((string)$key); } protected static function request($longitude, $latitude, $key) { $postStr = json_encode([ 'lon' => round($longitude, 6), 'lat' => round($latitude, 6), 'ver' => 1, ], JSON_UNESCAPED_UNICODE); $url = 'https://api.tianditu.gov.cn/geocoder?postStr=' . rawurlencode($postStr) . '&type=geocode&tk=' . rawurlencode($key); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 8); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'chufang-server/1.0'); $body = curl_exec($ch); $errno = curl_errno($ch); curl_close($ch); if ($errno || $body === false || $body === '') { throw new \Exception('地址解析失败,请稍后重试'); } $json = json_decode($body, true); if (!is_array($json) || !isset($json['status']) || (string)$json['status'] !== '0' || empty($json['result']) || !is_array($json['result'])) { $message = is_array($json) && !empty($json['msg']) ? $json['msg'] : '地址解析失败'; throw new \Exception($message); } return $json['result']; } protected static function matchArea(array $component) { $district = self::findDistrict($component); if (!$district) { throw new \Exception('未能匹配区县,请手动选择所在地区'); } $city = Area::where('id', $district['pid'])->find(); $province = $city ? Area::where('id', $city['pid'])->find() : null; if (!$city || !$province || (int)$city['level'] !== 2 || (int)$province['level'] !== 1) { throw new \Exception('未能匹配省市区,请手动选择所在地区'); } return [ 'province' => $province['name'], 'city' => $city['name'], 'district' => $district['name'], ]; } protected static function findDistrict(array $component) { $countyId = self::adminCode(isset($component['county_code']) ? $component['county_code'] : ''); if ($countyId) { $district = Area::where('id', $countyId)->where('level', 3)->find(); if ($district) { return $district; } } $county = trim(isset($component['county']) ? $component['county'] : ''); if ($county === '') { return null; } $cityId = self::adminCode(isset($component['city_code']) ? $component['city_code'] : ''); $query = Area::where('name', $county)->where('level', 3); if ($cityId) { $query->where('pid', $cityId); } return $query->find(); } protected static function detailAddress(array $result, array $component) { $segments = []; $adminNames = []; foreach (['province', 'city', 'county'] as $field) { $name = trim(isset($component[$field]) ? (string)$component[$field] : ''); if ($name !== '') { $adminNames[] = $name; } } foreach (['town', 'road', 'address', 'poi'] as $field) { $segment = trim(isset($component[$field]) ? (string)$component[$field] : ''); $segment = self::stripAdmin($segment, $adminNames); if ($segment === '' || self::isRelative($segment)) { continue; } $replaced = false; foreach ($segments as $index => $exist) { if (mb_strpos($segment, $exist) !== false) { $segments[$index] = $segment; $replaced = true; break; } if (mb_strpos($exist, $segment) !== false) { $replaced = true; break; } } if (!$replaced) { $segments[] = $segment; } } if ($segments) { return implode('', $segments); } $detail = isset($result['formatted_address']) ? $result['formatted_address'] : ''; foreach (['province', 'city', 'county'] as $field) { $part = trim(isset($component[$field]) ? (string)$component[$field] : ''); if ($part !== '') { $detail = preg_replace('/' . preg_quote($part, '/') . '/u', '', $detail, 1); } } return trim($detail); } protected static function stripAdmin($segment, array $adminNames) { foreach ($adminNames as $name) { if ($name !== '' && mb_strpos($segment, $name) !== false) { $segment = str_replace($name, '', $segment); } $short = preg_replace('/(省|市|自治区|特别行政区)$/u', '', $name); if ($short === '' || $short === $name || mb_strlen($short) < 2) { continue; } if ($segment === $short) { return ''; } $suffixLength = mb_strlen($short); if (mb_strlen($segment) > $suffixLength && mb_substr($segment, -$suffixLength) === $short) { $segment = mb_substr($segment, 0, mb_strlen($segment) - $suffixLength); } } return trim($segment); } protected static function isRelative($text) { return (bool)preg_match('/[东西南北].{0,4}约?\d+米/u', $text); } protected static function adminCode($code) { $digits = preg_replace('/\D/', '', (string)$code); if (strlen($digits) < 6) { return 0; } return (int)substr($digits, -6); } protected static function gcj02ToWgs84($longitude, $latitude) { if (self::outOfChina($longitude, $latitude)) { return [$longitude, $latitude]; } $offset = self::offset($longitude, $latitude); return [$longitude * 2 - $offset[0], $latitude * 2 - $offset[1]]; } protected static function offset($longitude, $latitude) { $a = 6378245.0; $ee = 0.00669342162296594323; $dlng = self::transformLng($longitude - 105.0, $latitude - 35.0); $dlat = self::transformLat($longitude - 105.0, $latitude - 35.0); $radLat = $latitude / 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 [$longitude + $dlng, $latitude + $dlat]; } protected static function outOfChina($longitude, $latitude) { return $longitude < 72.004 || $longitude > 137.8347 || $latitude < 0.8293 || $latitude > 55.8271; } protected 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 * sin($lat * M_PI / 30.0)) * 2.0 / 3.0; return $ret; } protected 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; } }