1

I try to calculate distance between 2 locations but I have an error with this formula

acos(sin(latA)*sin(latB) + cos(latA)*cos(latB)*cos(abs(longB-longA)))

Here is my function to calculate distances :

def distanceGPS(latA, longA, latB, longB):
    """Retourne la distance en Kilomètres entre les 2 points A et B connus grâce à
       leurs coordonnées GPS (en radians).
    """
    latA, longA, latB, longB = deg2rad(latA, longA, latB, longB)

    print(latA)
    print(longA)
    print(latB)
    print(longB)

    # Rayon de la terre en mètres (sphère IAG-GRS80)
    RT = 6378.137
    # angle en radians entre les 2 points
    S = acos(sin(latA)*sin(latB) + cos(latA)*cos(latB)*cos(abs(longB-longA)))

    # distance entre les 2 points, comptée sur un arc de grand cercle
    return (S*RT)

Result of print :

lat    0.651706 
dtype: float64
lon   -0.079956
dtype: float64
0.6517058798628295
-0.07995634999527296

Here is my function to convert degrees to radian :

def deg2rad(latA, longA, latB, longB):
    """Convertit un angle "degrés décimaux" en "radians"
    """
    return latA/180*pi, longA/180*pi, latB/180*pi, longB/180*pi

def test_prediction(corpus, lat_moy, long_moy, i):
    """
    Cette fonction test si la localisation predite est a une distance inferieure a 100km de la localisation correct. 
    Si oui, alors on defini la prediction comme correct
    Si non, alors on defini la prediction comme non correct
    """

    for word in corpus.correct_localisation[i]:
        loc = geolocator.geocode(word)

        if loc is not None and distanceGPS(lat_moy, long_moy, loc.latitude, loc.longitude) <= 100:
            corpus.at[i, "test_pred"] = "OK"
            return
        else:
            corpus.at[i, "test_pred"] = "NOK"

    return

My error is :

> --------------------------------------------------------------------------- ValueError                                Traceback (most recent call
> last) <ipython-input-18-75f41b231cbd> in <module>
>      11     df = coordonnees_article(corpus['article'][i])
>      12     lat_moy, long_moy = position_moyenne(outliers_coordonnees(df))
> ---> 13     test_prediction(corpus, lat_moy, long_moy, i)
>      14     print(i)
> 
> <ipython-input-17-47270c034d33> in test_prediction(corpus, lat_moy,
> long_moy, i)
>      11         print(loc)
>      12  #       try:
> ---> 13         if loc is not None and distanceGPS(lat_moy, long_moy, loc.latitude, loc.longitude) <= 100:
>      14             corpus.at[i, "test_pred"] = "OK"
>      15             return
> 
> <ipython-input-16-129df29f0484> in distanceGPS(latA, longA, latB,
> longB)
>      13     RT = 6378.137
>      14     # angle en radians entre les 2 points
> ---> 15     S = acos(sin(latA)*sin(latB) + cos(latA)*cos(latB)*cos(abs(longB-longA)))
>      16 
>      17     # distance entre les 2 points, comptée sur un arc de grand cercle
> 
> ValueError: math domain error

Thank you for your help

martineau
  • 119,623
  • 25
  • 170
  • 301
Clement Ros
  • 329
  • 1
  • 3
  • 17

1 Answers1

2

For the "weird" cases that you sometimes get, it's due to floating point inaccuracies. For example if you run:

1.2 - 1.0 = 0.19999999999999996

We'd obviously expect it to say 0.2 but because of the way floating point numbers are represented (using a fixed number of binary digits), we get the result above.

How can you deal with that? You can either use the decimal module or round your values to a smaller number of decimal places.

If you use the decimal module in python:

from decimal import *

# the following sets the number of decimal places to 8
getcontext().prec = 8

# if you run
sin(latA)*sin(latB) + cos(latA)*cos(latB)*cos(abs(longB-longA))
# the output is
0.999999999999954

# if you use the decimal module
# (please note I am multiplying by 1 at the end)
Decimal(sin(latA)*sin(latB) + cos(latA)*cos(latB)*cos(abs(longB-longA)))*1
# the output is
Decimal('1.00000000000')

You can also use the round function and round your number to fewer decimal places:

x = 0.19999999999999996
round(x,8)
# the output is
0.2

In your case, although I did not get the domain error, I think the reason you are getting it is because your computer may calculate a value that is 1.000000000004 (for example). Consequently, when you try to take acos(1.0000000004) you get the domain error. Using either of the methods above to "round" your answer before invoking acos would likely solve the problem you are facing.

Vlad
  • 151
  • 2
  • 6