之前做过一个类似的应用,今天翻出来看了看,觉得写的不是很合理,于是重新考虑后写了一个查询的存储过程。
表就不描述,
过程如下:
-- ----------------------------
-- Procedure structure for DIS
-- ----------------------------
DROP PROCEDURE IF EXISTS `DIS`;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `DIS`(IN `_lat` varchar(11),IN `_lng` varchar(11),IN `_ras` int,INOUT `_data` text)
BEGIN
DECLARE _sql text ;
DECLARE EARTH_RADIUS VARCHAR(10) ;
DECLARE _range VARCHAR(16);
DECLARE lngR VARCHAR(16);
DECLARE maxLat VARCHAR(16);
DECLARE minLat VARCHAR(16);
DECLARE maxLng VARCHAR(16);
DECLARE minLng VARCHAR(16);
SET EARTH_RADIUS = 6378.137;
SET _range = 180 / pi() * _ras / EARTH_RADIUS;
SET lngR = _range / cos(_lat * pi() / 180);
SET maxLat = _lat + _range;
SET minLat = _lat - _range;
SET maxLng = _lng + lngR ;
SET minLng = _lng - lngR ;
SET @_sql = CONCAT('SELECT
*,
ceil(
2 * asin(
sqrt(
pow(sin((((',_lat,' * PI() / 180.0) - (',_lat,' * PI() / 180.0))) / 2),2)
+ cos(',_lat,' * PI() / 180.0) * cos(lat * PI() / 180.0) * pow(sin(((',_lng,' * PI() / 180.0) - (lng * PI() / 180.0)) / 2),2)
)
) * ',EARTH_RADIUS,' * 1000
) AS di
FROM dis
WHERE lat BETWEEN ',minLat,' AND ',maxLat,' AND lng BETWEEN ',minLng,' AND ',maxLng,'
ORDER BY di
LIMIT 0,10');
PREPARE stmt FROM @_sql;
EXECUTE stmt;
END
;;
DELIMITER ;
调用 :
CALL DIS(_lat,_lng,_ras,@_data);
参数说明:
_lat,经度;
_lng,纬度;
_ras,半径(单位为km);
@_data ,接受返回
返回值中di为距离已经排序,由近及远,单位为m。
如果不使用存储过程可以拆分成PHP程序:
define("EARTH_RADIUS",6378.137);
/**
* 获取距离四个坐标
* @param $lon
* @param $lat
* @param int $distance 默认1KM的距离
* @return array
*/
function getCoor($lng,$lat,$distance = 1){
$range = 180 / pi() * $distance / EARTH_RADIUS;
$lngR = $range / cos($lat * pi() / 180);
$data = array();
$data["maxLat"] = $lat + $range;
$data["minLat"] = $lat - $range;
$data["maxLng"] = $lng + $lngR ;//最大经度
$data["minLng"] = $lng - $lngR ;//最小经度
return $data;
}
首先先获取一个正方型的坐标区域,然后通过上面 @_sql中sql拼接成一个sql语句,执行即可。
备注一下,这里得到的是一个正方型而非圆形半径的结果,可以再后期的时候做一个二次的筛除即可,例如array_filter(),这里就不再多描述了。