Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-7811

geo distance calculation

    • Type: Icon: Question Question
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Geo
    • Labels:

      we can't seem to figure out the calculation mongodb uses when it returns the distance ("dis") key for a geoNear command

      we need this distance, but we want to use the $nearSphere find query instead of the geoNear command
      since the find query doesn't return the calculated distance, we have to calculate it on our own
      the problem is that any formula we use to calculate the distance doesn't match the distances calculated by mongodb
      the manually calculated distance is very close to the one returned by geoNear, but never exactly the same (they're not even all linearly offset, so it would mean a different order of documents)

      these are the formulas we tried so far, which all return the same results (among themselves), but none match mongodb's geoNear distance numbers:

      private static $cf_earth_km = 40074;
      $theta = $lng1 - $lng2;
      $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
      $dist = acos($dist);
      $dist = rad2deg($dist);
      $km = self::degree2km($dist);
      
      public static function degree2km($degree, $precision = false) {
      	$km = $degree * self::$cf_earth_km / 360;
      	return $precision ? floatval(sprintf('%.' . intval($precision) . 'f', $km)) : $km;
      }
      
      private static $radius_earth_km = 6378;
      
      $pi80 = M_PI / 180;
      $lat1 *= $pi80;
      $lng1 *= $pi80;
      $lat2 *= $pi80;
      $lng2 *= $pi80;
      
      $dlat = $lat2 - $lat1;
      $dlng = $lng2 - $lng1;
      $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
      $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
      $km = self::$radius_earth_km * $c;
      

      so the question is, how does mongodb calculate the distance returned by geoNear?

      looking at mongodb source code, i think i successfully traced the actual calculation to a S2LatLng::GetDistance function (http://code.google.com/p/s2-geometry-library/source/browse/geometry/s2latlng.cc?r=c872048da5d12db6058022e73f9c5dcbfdd93079), but it's basically the same as the first custom calculation function we tried

      distance calculation test:
      monospaced
      mongo geoNear: 1.10000 km | mongo source (S2LatLng::GetDistance): 0.95942 km | custom 1: 0.96000 km | custom 2: 0.96000 km
      mongo geoNear: 1.32000 km | mongo source (S2LatLng::GetDistance): 1.52556 km | custom 1: 1.53000 km | custom 2: 1.53000 km
      mongo geoNear: 1.46000 km | mongo source (S2LatLng::GetDistance): 1.62114 km | custom 1: 1.62000 km | custom 2: 1.62000 km
      mongo geoNear: 1.55000 km | mongo source (S2LatLng::GetDistance): 1.72047 km | custom 1: 1.72000 km | custom 2: 1.72000 km
      mongo geoNear: 1.56000 km | mongo source (S2LatLng::GetDistance): 1.47956 km | custom 1: 1.48000 km | custom 2: 1.48000 km
      mongo geoNear: 1.56000 km | mongo source (S2LatLng::GetDistance): 1.45865 km | custom 1: 1.46000 km | custom 2: 1.46000 km
      mongo geoNear: 1.57000 km | mongo source (S2LatLng::GetDistance): 1.33709 km | custom 1: 1.34000 km | custom 2: 1.34000 km
      mongo geoNear: 1.66000 km | mongo source (S2LatLng::GetDistance): 1.46648 km | custom 1: 1.47000 km | custom 2: 1.47000 km
      mongo geoNear: 1.67000 km | mongo source (S2LatLng::GetDistance): 1.82464 km | custom 1: 1.82000 km | custom 2: 1.82000 km
      mongo geoNear: 1.68000 km | mongo source (S2LatLng::GetDistance): 1.91546 km | custom 1: 1.92000 km | custom 2: 1.92000 km
      mongo geoNear: 1.83000 km | mongo source (S2LatLng::GetDistance): 1.63233 km | custom 1: 1.63000 km | custom 2: 1.63000 km
      mongo geoNear: 1.83000 km | mongo source (S2LatLng::GetDistance): 1.62369 km | custom 1: 1.62000 km | custom 2: 1.62000 km
      mongo geoNear: 1.83000 km | mongo source (S2LatLng::GetDistance): 1.61788 km | custom 1: 1.62000 km | custom 2: 1.62000 km
      mongo geoNear: 1.83000 km | mongo source (S2LatLng::GetDistance): 1.63008 km | custom 1: 1.63000 km | custom 2: 1.63000 km
      mongo geoNear: 1.88000 km | mongo source (S2LatLng::GetDistance): 2.02586 km | custom 1: 2.03000 km | custom 2: 2.03000 km
      mongo geoNear: 1.89000 km | mongo source (S2LatLng::GetDistance): 1.64674 km | custom 1: 1.65000 km | custom 2: 1.65000 km
      mongo geoNear: 1.89000 km | mongo source (S2LatLng::GetDistance): 1.61495 km | custom 1: 1.61000 km | custom 2: 1.61000 km
      mongo geoNear: 1.90000 km | mongo source (S2LatLng::GetDistance): 1.67249 km | custom 1: 1.67000 km | custom 2: 1.67000 km
      mongo geoNear: 1.90000 km | mongo source (S2LatLng::GetDistance): 2.02528 km | custom 1: 2.03000 km | custom 2: 2.03000 km
      mongo geoNear: 1.90000 km | mongo source (S2LatLng::GetDistance): 1.65547 km | custom 1: 1.66000 km | custom 2: 1.66000 km
      mongo geoNear: 1.90000 km | mongo source (S2LatLng::GetDistance): 2.01676 km | custom 1: 2.02000 km | custom 2: 2.02000 km
      mongo geoNear: 1.90000 km | mongo source (S2LatLng::GetDistance): 1.63483 km | custom 1: 1.63000 km | custom 2: 1.63000 km
      mongo geoNear: 1.91000 km | mongo source (S2LatLng::GetDistance): 1.70989 km | custom 1: 1.71000 km | custom 2: 1.71000 km
      mongo geoNear: 1.95000 km | mongo source (S2LatLng::GetDistance): 1.86317 km | custom 1: 1.86000 km | custom 2: 1.86000 km
      monospaced

            Assignee:
            hari.khalsa@10gen.com hari.khalsa@10gen.com
            Reporter:
            pcmaniac Nemanja Dubravac
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: