【问题标题】:Is there any easy way to make GPS coordinates coarse?有什么简单的方法可以使 GPS 坐标变粗吗?
【发布时间】:2011-12-22 13:23:47
【问题描述】:

我正在开发一个使用 GPS 坐标作为排行榜的 iPhone 应用程序。我不需要精确的坐标 --- 实际上,为了保护用户隐私,我永远不希望坐标精确。

我将 kCLLocationAccuracyThreeKilometers 指定为 desiredAccuracy,但是当 GPS 处于活动状态时,它似乎也可以在设备拥有它时获取确切位置。

问题:我可以使用任何简单的算法来使 GPS 数据更粗略吗?比如说,把它细化到 3 公里。

如果我只是放大数字并删除小数点并再次缩小它们,它会在世界某些地方比其他地方更粗糙。

谢谢!

【问题讨论】:

  • 请记住,平行线有不同的长度。我的意思是在赤道附近(纬度~0°),单经度比两极附近单经度的距离更大。在两极(纬度 +/-90°),经度甚至失去了意义。

标签: geolocation


【解决方案1】:

虽然上面 Mark 的回答很有用,但它仍然没有产生具有一致结果的公式,因为它依赖于随机数生成器。

我的好友为此提供了最佳答案:

根据粒度将 lat,lon 舍入到最接近的有效数字,但这会导致某个位置附近的所有 lat/lons 都在同一位置结束。此方法将使用 lat/lon 中两点之间的距离来计算 lat lon 的舍入。使用下面相同的公式并将路线设置为 0,那么距离就是您的距离粒度。计算得到的新纬度/经度减去两个纬度/经度以获得纬度的舍入量。然后将航向设置为 90 并重新计算并从旧的 lat/lon 中减去新的 lat/lon 以获得 lon 的舍入量。

这是 C++ 代码:

class LocationUtility
{
  public: static Location getLocationNow()
  {
    Location location;

    if(context != null)
    {
      double latitude = 0;
      double longitude = 0;
      ::of_getCurrentLocation(&latitude, &longitude);

      location.setLatitude(latitude);
      location.setLongitude(longitude);

      location = makeLocationCoarse(location);
    }

    return location;
  }

  public: static Location makeLocationCoarse(const Location& location)
  {
      double granularityInMeters = 3 * 1000;
      return makeLocationCoarse(location, granularityInMeters);
  }

  public: static Location makeLocationCoarse(const Location& location,
             double granularityInMeters)
  {
    Location courseLocation;

    if(location.getLatitude() == (double)0 && 
      location.getLongitude() == (double)0)
    {
      // Special marker, don't bother.
    }
    else
    {
      double granularityLat = 0;
      double granularityLon = 0;
      {
        // Calculate granularityLat
        {
          double angleUpInRadians = 0;
          Location newLocationUp = getLocationOffsetBy(location, 
            granularityInMeters, angleUpInRadians);

          granularityLat = location.getLatitude() - 
            newLocationUp.getLatitude();

          if(granularityLat < (double)0)
          {
            granularityLat = -granularityLat;
          }
        }

        // Calculate granularityLon
        {
          double angleRightInRadians = 1.57079633;
          Location newLocationRight = getLocationOffsetBy(location,
            granularityInMeters, angleRightInRadians);

          granularityLon = location.getLongitude() - 
            newLocationRight.getLongitude();

          if(granularityLon < (double)0)
          {
            granularityLon = -granularityLon;
          }
        }
      }

      double courseLatitude = location.getLatitude();
      double courseLongitude = location.getLongitude();
      {
        if(granularityLon == (double)0 || granularityLat == (double)0)
        {
          courseLatitude = 0;
          courseLongitude = 0;
        }
        else
        {
          courseLatitude = (int)(courseLatitude / granularityLat) * 
            granularityLat;

          courseLongitude = (int)(courseLongitude / granularityLon) * 
            granularityLon;
        }
      }
      courseLocation.setLatitude(courseLatitude);
      courseLocation.setLongitude(courseLongitude);
    }

    return courseLocation;
  }

  // http://www.movable-type.co.uk/scripts/latlong.html
  private: static Location getLocationOffsetBy(const Location& location,
    double offsetInMeters, double angleInRadians)
  {
    Location newLocation;

    double lat1 = location.getLatitude();
    double lon1 = location.getLongitude();

    lat1 = deg2rad(lat1);
    lon1 = deg2rad(lon1);

    double distanceKm = offsetInMeters / (double)1000;
    const double earthRadiusKm = 6371;

    double lat2 = asin( sin(lat1)*cos(distanceKm/earthRadiusKm) + 
      cos(lat1)*sin(distanceKm/earthRadiusKm)*cos(angleInRadians) );

    double lon2 = lon1 + 
      atan2(sin(angleInRadians)*sin(distanceKm/earthRadiusKm)*cos(lat1), 
      cos(distanceKm/earthRadiusKm)-sin(lat1)*sin(lat2));

    lat2 = rad2deg(lat2);
    lon2 = rad2deg(lon2);

    newLocation.setLatitude(lat2);
    newLocation.setLongitude(lon2);

    return newLocation;
  }

  private: static double rad2deg(double radians)
  {
    static double ratio = (double)(180.0 / 3.141592653589793238);
    return radians * ratio;
  }

  private: static double deg2rad(double radians)
  {
    static double ratio = (double)(180.0 / 3.141592653589793238);
    return radians / ratio;
  }

  /*
  public: static void testCoarse()
  {
    Location vancouver(49.2445, -123.099146);
    Location vancouver2 = makeLocationCoarse(vancouver);

    Location korea(37.423938, 126.692488);
    Location korea2 = makeLocationCoarse(korea);

    Location hiroshima(34.3937, 132.464);
    Location hiroshima2 = makeLocationCoarse(hiroshima);

    Location zagreb(45.791958, 15.935786);
    Location zagreb2 = makeLocationCoarse(zagreb);

    Location anchorage(61.367778, -149.900208);
    Location anchorage2 = makeLocationCoarse(anchorage);
  }*/
};

【讨论】:

    【解决方案2】:

    这与上一个问题非常相似 Rounding Lat and Long to Show Approximate Location in Google Maps

    如果您假设地球是一个球体(可能足以解决这个问题),那么您只需要计算一个与给定纬度和经度有一定角度距离的位置。选择一个距离和一个(随机)方向,并使用距离公式计算新位置。

    这里很好地讨论了相反的问题(两个纬度/经度点之间的距离):http://mathforum.org/library/drmath/view/51756.html

    从那里找到距离给定点指定距离的点应该相对简单。

    【讨论】:

      【解决方案3】:

      swinefeaster 的答案是可以的,但是不需要这么复杂的数学。如果您要四舍五入到网格,则纬度会在地球上的所有点以恒定的量变化。根据您与赤道的距离,经度会发生不同程度的变化。

      以下代码将纬度和经度捕捉到任意网格大小

      double EARTH_RADIUS_KM = 6371;
      
      double GRID_SIZE_KM = 1.6; // <----- Our grid size in km..
      
      double DEGREES_LAT_GRID = Math.toDegrees(GRID_SIZE_KM / EARTH_RADIUS_KM);
      //     ^^^^^^ This is constant for a given grid size.
      
      public Location snapToGrid(Location myLoc) {
        double cos = Math.cos(Math.toRadians(myLoc.latitude));
      
        double degreesLonGrid = DEGREES_LAT_GRID / cos;
      
        return new Location (
            Math.round(myLoc.longitude / degreesLonGrid) * degreesLonGrid,
            Math.round(myLoc.latitude / DEGREES_LAT_GRID) * DEGREES_LAT_GRID);
      
      }
      

      请注意,如果您在极点(当 cos 函数接近零时),这将失败。根据您的网格大小,当您接近 +/- 90 度的纬度时,结果会变得不可预测。处理这个是留给读者的练习:)

      【讨论】:

        【解决方案4】:

        我尝试在 Ruby 中实现该解决方案,但在我的情况下,粗坐标与实际坐标有很大的不同。粗坐标仅在 lat 变化时发生变化,但当 lat 保持不变且长时间移动时,粗坐标保持不变。万一有人可以检查下面的代码,也许我的编码不好。

        class CoarseLocation
        
          AREA_LIMIT = 1000
        
          class << self
        
            def make_location_coarse(lat, lon)
        
              if lat.nil? && lon.nil?
                raise InvalidParamsError
              end
        
              location = [lat.to_f, lat.to_f]
        
              new_location_up =  get_location_by_offset(location, AREA_LIMIT, 0)
        
              granularityLat = location[0] - new_location_up[0]
        
              if granularityLat < 0
                granularityLat = -granularityLat
              end
        
        
              new_location_right = get_location_by_offset(location, AREA_LIMIT, 1.57079633)
        
              granularityLon = location[1] - new_location_right[1]
        
              if(granularityLon < 0)
                granularityLon = -granularityLon
              end
        
              course_lat = location[0]
              course_lon = location[1]
        
              if(granularityLat ==  0.0) || (granularityLon == 0.0)
                course_lat = 0
                course_lon = 0
              else
                course_lat = (course_lat / granularityLat).to_i * granularityLat
                course_lon = (course_lon / granularityLon).to_i * granularityLon
              end
        
              [course_lat, course_lon]
            end
        
            def get_location_by_offset(location, offset, angle)
              lat_radius = location[0] * Math::PI / 180
              lon_radius = location[1] * Math::PI / 180
        
              distance = (offset / 1000).to_f
              earth_radius = 6371
        
              lat_radius_1 = (Math::asin( Math::sin(lat_radius) * Math::cos(distance/earth_radius) + Math::cos(lat_radius) * Math::sin(distance/earth_radius) * Math::cos(angle))).to_f
              lon_radius_1 = (lon_radius + Math::atan2(Math::sin(angle)*Math::sin(distance/earth_radius)*Math::cos(lat_radius), Math::cos(distance/earth_radius) - Math::sin(lat_radius)*Math::sin(lat_radius_1))).to_f
        
              new_lat = lat_radius_1 * 180 / Math::PI
              new_lon = lon_radius_1 * 180 / Math::PI
        
              return [new_lat.to_f, new_lon.to_f]
        
            end
          end
        end
        

        Location 字段始终是由 2 个元素组成的 ab 数组,其中 [0] 为 lat,[1] 为 long。

        【讨论】:

          猜你喜欢
          • 2011-08-05
          • 1970-01-01
          • 1970-01-01
          • 2020-01-10
          • 1970-01-01
          • 1970-01-01
          • 2013-09-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多