Is there any easy way to make GPS coordinates coarse?

落花浮王杯 提交于 2019-12-11 00:29:59

问题


I'm working on an iPhone app that is using GPS coordinates for leaderboards. I don't need the coordinates to be exact --- actually I don't ever want the coordinates to be exact, to protect user privacy.

I am specifying kCLLocationAccuracyThreeKilometers for desiredAccuracy, but when the GPS is active it seems it can also pick up the exact location when the device has it.

QUESTION: Is there any easy algorithm I can use to make the GPS data more coarse? Say, make it granular to 3km.

If I just scale the numbers up and remove decimal points and scale them down again it will make it more coarse in some parts of the world than others.

Thanks!


回答1:


While Mark's answer above was a useful, it still did not yield a formula with consistent results because it relied on a random number generator.

My buddy provided the best answer for this:

Round the lat,lon to the nearest significant figure depending on the granularity, but this would result in all the lat/lons near a certain location, wind up in the same location. This method would use the distance between two points in lat/lon to calculate the rounding of the lat lon. Use the same formula below and set the course to 0, then the distance is your distance granularity. Calculate the resulting new lat/lon subtract the two lat/lons to get the rounding amount for lat. Then set the heading to 90 and recalculate and subtract the new lat/lon from the old to get the rounding amount for lon.

And here's the C++ code:

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:


This is very similar to a previous question Rounding Lat and Long to Show Approximate Location in Google Maps

If you assume the earth is a sphere (probably adequate for this problem), then you just need to calculate a location which is a certain angular distance from the given latitude and longitude. Pick a distance and a (random) direction, and calculate the new location by using the distance formula.

There's good discussion of the opposite problem (distance between two latitude/longitude points) here: http://mathforum.org/library/drmath/view/51756.html

It ought to be relatively straightforward to go from there to finding a point a specified distance away from the given point.




回答3:


The answer from swinefeaster is ok, but there is no need for such complex maths. If you're rounding to a grid, then the latitude changes by constant amounts at all points on the planet. Longitude changes by different amounts according to how far you are from the equator.

The following code snaps latitude and longitude to an arbitrary grid size

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);

}

Note that this will fail in the case where you are at the Pole (when the cos function approaches zero). Depending on your grid size, the results become unpredictable as you approach a latitude of +/- 90 degrees. Handling this is an exercise left for the reader :)




回答4:


I try to implemente the solution in Ruby but in my case, the coarse coordinate vs real have a huge difference. the coarse coordinate only change when the lat change but when lat stay the same and long move, coarse remain the same. In case someone can check the code below, perhaps I made a bad coding.

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 field is always ab array of 2 elements in which [0] is lat and [1] is long.



来源:https://stackoverflow.com/questions/8018788/is-there-any-easy-way-to-make-gps-coordinates-coarse

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!