3

这是一个关于使用半正弦公式计算地球上经纬度两点之间距离的问题,用于需要“找到我最近的”功能的项目。

在这篇文章的 MySQL 中,haversine 公式得到了很好的讨论和解决。

然后我问了这个关于将其转换为存储函数的问题,以便它可用于未来的项目,而无需查找、记住或重新输入长格式的公式。

都很好。除了我的函数在结果上(略有不同)与直接在查询中输入公式不同,其他所有条件都相同。为什么是这样?

所以这是我写的函数:

DELIMITER $$

DROP FUNCTION IF EXISTS haversine $$

CREATE FUNCTION `haversine`
    (fromLatitude FLOAT,
     fromLongitude FLOAT,
     toLatitude FLOAT,
     toLongitude FLOAT,
     unit VARCHAR(20)
     )
    RETURNS FLOAT
    DETERMINISTIC    
    COMMENT 'Returns the distance on the Earth between two known points of longitude and latitude'
    BEGIN
    DECLARE radius FLOAT;
    DECLARE distance FLOAT;

    IF unit = 'MILES' THEN SET radius = '3959';
    ELSEIF (unit = 'NAUTICAL_MILES' OR unit='NM') THEN SET radius = '3440.27694';   
    ELSEIF (unit = 'YARDS' OR unit='YD') THEN SET radius = '6967840';
    ELSEIF (unit = 'FEET' OR unit='FT') THEN SET radius = '20903520';
    ELSEIF (unit = 'KILOMETRES' OR unit='KILOMETERS' OR unit='KM') THEN SET radius = '6371.3929';
    ELSEIF (unit = 'METRES' OR UNIT='METERS' OR unit='M') THEN SET radius = '6371392.9';
    ELSE SET radius = '3959'; /* default to miles */
    END IF;

    SET distance = (radius * ACOS(COS(RADIANS(fromLatitude)) * COS(RADIANS(toLatitude)) * COS(RADIANS(toLongitude) - RADIANS(fromLongitude)) + SIN(RADIANS(fromLatitude)) * SIN(RADIANS(toLatitude))));

    RETURN distance;
    END$$

DELIMITER ;

这是一组测试查询,用于查找伦敦眼和白金汉宫之间的距离,仅作为示例。显然,通常您会用您想要比较的地理定位“事物”数据库中的字段替换目的地。

SET @milesModifier = 3959;

SET @myLat = 51.503228;
SET @myLong = -0.119703;

SET @destLat = 51.501267;  
SET @destLong = -0.142697;

SELECT  @kilometerModifier AS radius,
    @myLat AS myLat,
    @myLong AS myLong,
    @destLat AS destLat,
    @destLong AS destLong,
    (@milesModifier * ACOS(COS(RADIANS(@myLat)) * COS(RADIANS(@destLat)) * COS(RADIANS(@destLong) - RADIANS(@myLong)) + SIN(RADIANS(@myLat)) * SIN(RADIANS(@destLat)))) AS longFormat,
    haversine(@myLat,@myLong,@destLat,@destLong,'MILES') AS distanceMiles,
    haversine(@myLat,@myLong,@destLat,@destLong,'NAUTICAL_MILES') AS distanceNautical,
    haversine(@myLat,@myLong,@destLat,@destLong,'KM') AS distanceKm,
    haversine(@myLat,@myLong,@destLat,@destLong,'METRES') AS distanceMetres,    
    haversine(@myLat,@myLong,@destLat,@destLong,'YARDS') AS distanceYards,
    haversine(@myLat,@myLong,@destLat,@destLong,'FEET') AS distanceFeet,
    haversine(@myLat,@myLong,@destLat,@destLong,'') AS distanceDefault

在示例中,我们使用英里 - 因此我们将半径(测试中的@milesModifier ,函数中的半径)精确设置为 3959。

我得到的结果很有趣(在 MySQL 5.2.6 社区版上),亮点:

| longFormat       | distanceMiles   |
|------------------|-----------------|
| 0.99826000106148 | 0.9982578754425 |

longFormat是查询中完成的数学运算,distanceMiles是函数的结果。

结果是不同的......好吧,所以就在项目中使用该函数而言,它是微不足道的,但我很想知道函数内部或外部的相同公式如何产生不同的结果。

我猜这与 FLOAT 的长度有关 - 它们没有在函数中指定,我尝试指定它们(最多 30,15)为我拥有的所有数字和输出提供足够的空间我期待 - 但结果仍然略有不同。

4

1 回答 1

5

FLOAT是一种近似数据类型 - 请参阅:

浮点值
数值类型的问题

尝试更改FLOATDECIMAL(30,15)确保您具有正确的精度。

如果你想深入讨论浮点,你可以试试这篇文章:

每个计算机科学家都应该知道的关于浮点运算的知识

于 2011-06-23T18:33:59.667 回答