sourcetip

파이어스토어에서 지역 "근처" 쿼리를 실행하는 방법은 무엇입니까?

fileupload 2023. 6. 17. 09:37
반응형

파이어스토어에서 지역 "근처" 쿼리를 실행하는 방법은 무엇입니까?

소방서의 새로운 소방서 데이터베이스는 기본적으로 위치 기반 지리 쿼리를 지원합니까? 즉, 10마일 이내의 위치를 찾거나 가장 가까운 위치를 50개 찾으십시오.

실시간 소방 기반 데이터베이스에 대한 기존 프로젝트가 몇 가지 있는 것으로 알고 있습니다. 지오파이어와 같은 프로젝트도 소방 저장소에 적용할 수 있습니까?

업데이트: Firestore는 현재 실제 GeoPoint 쿼리를 지원하지 않으므로 아래 쿼리가 성공적으로 실행되는 동안 경도가 아닌 위도로만 필터링하므로 근처에 없는 많은 결과를 반환합니다.가장 좋은 해결책은 지오해시를 사용하는 것입니다.자신과 유사한 작업을 수행하는 방법을 알아보려면 이 비디오를 보십시오.

이 작업은 쿼리보다 작은 경계 상자를 작성하여 수행할 수 있습니다.효율성에 대해서는 말씀드릴 수 없습니다.

참고로 ~1마일에 대한 lat/long 오프셋의 정확도를 검토해야 하지만, 이를 위한 빠른 방법은 다음과 같습니다.

SWIFT 3.0 버전

func getDocumentNearBy(latitude: Double, longitude: Double, distance: Double) {

    // ~1 mile of lat and lon in degrees
    let lat = 0.0144927536231884
    let lon = 0.0181818181818182

    let lowerLat = latitude - (lat * distance)
    let lowerLon = longitude - (lon * distance)

    let greaterLat = latitude + (lat * distance)
    let greaterLon = longitude + (lon * distance)

    let lesserGeopoint = GeoPoint(latitude: lowerLat, longitude: lowerLon)
    let greaterGeopoint = GeoPoint(latitude: greaterLat, longitude: greaterLon)

    let docRef = Firestore.firestore().collection("locations")
    let query = docRef.whereField("location", isGreaterThan: lesserGeopoint).whereField("location", isLessThan: greaterGeopoint)

    query.getDocuments { snapshot, error in
        if let error = error {
            print("Error getting documents: \(error)")
        } else {
            for document in snapshot!.documents {
                print("\(document.documentID) => \(document.data())")
            }
        }
    }

}

func run() {
    // Get all locations within 10 miles of Google Headquarters
    getDocumentNearBy(latitude: 37.422000, longitude: -122.084057, distance: 10)
}

업데이트: Firestore는 현재 실제 GeoPoint 쿼리를 지원하지 않으므로 아래 쿼리가 성공적으로 실행되는 동안 경도가 아닌 위도로만 필터링하므로 근처에 없는 많은 결과를 반환합니다.가장 좋은 해결책은 지오해시를 사용하는 것입니다.자신과 유사한 작업을 수행하는 방법을 알아보려면 이 비디오를 보십시오.

(먼저 이 게시물의 모든 코드에 대해 사과드리며, 이 답변을 읽는 모든 사용자가 이 기능을 쉽게 복제할 수 있기를 바랍니다.)

OP가 가졌던 동일한 우려를 해결하기 위해 처음에 저는 GeoFire 라이브러리를 Firestore와 함께 작업하도록 조정했습니다(그 라이브러리를 보면 지리 정보에 대해 많은 것을 배울 수 있습니다).그리고 나서 저는 위치가 정확한 원으로 반환되는 것에 대해 별로 신경 쓰지 않는다는 것을 깨달았습니다.저는 단지 '근처' 위치를 찾을 수 있는 방법을 원했습니다.

제가 이것을 깨닫는 데 얼마나 오래 걸렸는지 믿을 수 없지만, SW 코너와 NE 코너를 사용하여 GeoPoint 필드에서 이중 불평등 쿼리를 수행하여 중앙점 주변의 경계 상자 내 위치를 찾을 수 있습니다.

그래서 아래와 같은 자바스크립트 함수를 만들었습니다(이것은 기본적으로 Ryan Lee의 답변의 JS 버전입니다).

/**
 * Get locations within a bounding box defined by a center point and distance from from the center point to the side of the box;
 *
 * @param {Object} area an object that represents the bounding box
 *    around a point in which locations should be retrieved
 * @param {Object} area.center an object containing the latitude and
 *    longitude of the center point of the bounding box
 * @param {number} area.center.latitude the latitude of the center point
 * @param {number} area.center.longitude the longitude of the center point
 * @param {number} area.radius (in kilometers) the radius of a circle
 *    that is inscribed in the bounding box;
 *    This could also be described as half of the bounding box's side length.
 * @return {Promise} a Promise that fulfills with an array of all the
 *    retrieved locations
 */
function getLocations(area) {
  // calculate the SW and NE corners of the bounding box to query for
  const box = utils.boundingBoxCoordinates(area.center, area.radius);

  // construct the GeoPoints
  const lesserGeopoint = new GeoPoint(box.swCorner.latitude, box.swCorner.longitude);
  const greaterGeopoint = new GeoPoint(box.neCorner.latitude, box.neCorner.longitude);

  // construct the Firestore query
  let query = firebase.firestore().collection('myCollection').where('location', '>', lesserGeopoint).where('location', '<', greaterGeopoint);

  // return a Promise that fulfills with the locations
  return query.get()
    .then((snapshot) => {
      const allLocs = []; // used to hold all the loc data
      snapshot.forEach((loc) => {
        // get the data
        const data = loc.data();
        // calculate a distance from the center
        data.distanceFromCenter = utils.distance(area.center, data.location);
        // add to the array
        allLocs.push(data);
      });
      return allLocs;
    })
    .catch((err) => {
      return new Error('Error while retrieving events');
    });
}

또한 위의 함수는 반환되는 각 위치 데이터에 .distanceFromCenter 속성을 추가하여 해당 거리가 원하는 범위 내에 있는지 확인하는 것만으로 원과 같은 동작을 수행할 수 있습니다.

저는 위의 기능에서 2개의 util 함수를 사용하고 있기 때문에 그들에 대한 코드도 여기 있습니다. (아래의 util 함수는 모두 GeoFire 라이브러리에서 수정된 것입니다.)

거리 측정:

/**
 * Calculates the distance, in kilometers, between two locations, via the
 * Haversine formula. Note that this is approximate due to the fact that
 * the Earth's radius varies between 6356.752 km and 6378.137 km.
 *
 * @param {Object} location1 The first location given as .latitude and .longitude
 * @param {Object} location2 The second location given as .latitude and .longitude
 * @return {number} The distance, in kilometers, between the inputted locations.
 */
distance(location1, location2) {
  const radius = 6371; // Earth's radius in kilometers
  const latDelta = degreesToRadians(location2.latitude - location1.latitude);
  const lonDelta = degreesToRadians(location2.longitude - location1.longitude);

  const a = (Math.sin(latDelta / 2) * Math.sin(latDelta / 2)) +
          (Math.cos(degreesToRadians(location1.latitude)) * Math.cos(degreesToRadians(location2.latitude)) *
          Math.sin(lonDelta / 2) * Math.sin(lonDelta / 2));

  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

  return radius * c;
}

boundingBoxCoordinations(): (아래에 붙여넣은 유틸리티도 여기에 더 많이 사용됩니다.)

/**
 * Calculates the SW and NE corners of a bounding box around a center point for a given radius;
 *
 * @param {Object} center The center given as .latitude and .longitude
 * @param {number} radius The radius of the box (in kilometers)
 * @return {Object} The SW and NE corners given as .swCorner and .neCorner
 */
boundingBoxCoordinates(center, radius) {
  const KM_PER_DEGREE_LATITUDE = 110.574;
  const latDegrees = radius / KM_PER_DEGREE_LATITUDE;
  const latitudeNorth = Math.min(90, center.latitude + latDegrees);
  const latitudeSouth = Math.max(-90, center.latitude - latDegrees);
  // calculate longitude based on current latitude
  const longDegsNorth = metersToLongitudeDegrees(radius, latitudeNorth);
  const longDegsSouth = metersToLongitudeDegrees(radius, latitudeSouth);
  const longDegs = Math.max(longDegsNorth, longDegsSouth);
  return {
    swCorner: { // bottom-left (SW corner)
      latitude: latitudeSouth,
      longitude: wrapLongitude(center.longitude - longDegs),
    },
    neCorner: { // top-right (NE corner)
      latitude: latitudeNorth,
      longitude: wrapLongitude(center.longitude + longDegs),
    },
  };
}

미터 대 경도 각도():

/**
 * Calculates the number of degrees a given distance is at a given latitude.
 *
 * @param {number} distance The distance to convert.
 * @param {number} latitude The latitude at which to calculate.
 * @return {number} The number of degrees the distance corresponds to.
 */
function metersToLongitudeDegrees(distance, latitude) {
  const EARTH_EQ_RADIUS = 6378137.0;
  // this is a super, fancy magic number that the GeoFire lib can explain (maybe)
  const E2 = 0.00669447819799;
  const EPSILON = 1e-12;
  const radians = degreesToRadians(latitude);
  const num = Math.cos(radians) * EARTH_EQ_RADIUS * Math.PI / 180;
  const denom = 1 / Math.sqrt(1 - E2 * Math.sin(radians) * Math.sin(radians));
  const deltaDeg = num * denom;
  if (deltaDeg < EPSILON) {
    return distance > 0 ? 360 : 0;
  }
  // else
  return Math.min(360, distance / deltaDeg);
}

랩 경도():

/**
 * Wraps the longitude to [-180,180].
 *
 * @param {number} longitude The longitude to wrap.
 * @return {number} longitude The resulting longitude.
 */
function wrapLongitude(longitude) {
  if (longitude <= 180 && longitude >= -180) {
    return longitude;
  }
  const adjusted = longitude + 180;
  if (adjusted > 0) {
    return (adjusted % 360) - 180;
  }
  // else
  return 180 - (-adjusted % 360);
}

@monkeybonkey가 처음으로 이 질문을 한 이후로 새로운 프로젝트가 도입되었습니다.이 프로젝트의 이름은 GEOfirestore입니다.

이 라이브러리를 사용하여 원 안에 있는 쿼리 문서와 같은 쿼리를 수행할 수 있습니다.

  const geoQuery = geoFirestore.query({
    center: new firebase.firestore.GeoPoint(10.38, 2.41),
    radius: 10.5
  });

GeoFirestore는 npm을 통해 설치할 수 있습니다.Firebase는 GeoFirestore에 대한 피어 종속성이기 때문에 별도로 설치해야 합니다.

$ npm install geofirestore firebase --save

현재로서는 그러한 쿼리를 수행할 방법이 없습니다.SO에는 이와 관련된 다른 질문이 있습니다.

GeoFire를 Firestore와 함께 사용할 수 있는 방법이 있습니까?

Firebase Cloud Firestore 컬렉션에서 가장 가까운 GeoPoints를 쿼리하는 방법은 무엇입니까?

GeoFire를 Firestore와 함께 사용할 수 있는 방법이 있습니까?

현재 Android 프로젝트에서 Firebase 팀이 네이티브 지원을 개발하는 동안 https://github.com/drfonfon/android-geohash 을 사용하여 지오해시 필드를 추가할 수 있습니다.

다른 질문에서 제안한 것처럼 Firebase Realtime Database를 사용하면 위치 및 다른 필드별로 결과 집합을 동시에 필터링할 수 없으므로 우선 Firestore로 전환하려는 주요 이유입니다.

2020년 말 현재 Firestore에서 지오쿼리를 수행하는 방법에 대한 문서도 있습니다.

iOS, Android 및 Web용 솔루션은 Firebase에서 만든 GeoFire 라이브러리의 슬림한 버전 위에 구축된 다음 다음 방법을 보여줍니다.

  • 지오해시 값 생성 및 Firestore에 저장
  • 특정 점 및 반지름에 대한 경계 상자의 지오해시 범위 결정
  • 이 지오해시 범위에서 쿼리 수행

이는 여기에 제시된 대부분의 다른 라이브러리보다 약간 낮은 수준이므로 일부 사용 사례에는 더 적합하고 다른 사용 사례에는 더 적합하지 않을 수 있습니다.

여전히 찾는 사람들을 돕기 위해 이 스레드를 납치하는 것.Firestore는 여전히 지리 기반 쿼리를 지원하지 않으며 GeoFirestore 라이브러리를 사용하는 것도 이상적이지 않습니다. 위치별로 검색할 수 있기 때문입니다.

저는 이것을 정리했습니다: https://github.com/mbramwell1/GeoFire-Android

기본적으로 위치와 거리를 사용하여 근처 검색을 수행할 수 있습니다.

QueryLocation queryLocation = QueryLocation.fromDegrees(latitude, longitude);
Distance searchDistance = new Distance(1.0, DistanceUnit.KILOMETERS);
geoFire.query()
    .whereNearTo(queryLocation, distance)
    .build()
    .get();

보고서에 더 많은 문서가 있습니다.제게 효과가 있으니 한번 해보세요, 당신이 필요로 하는 것을 할 수 있기를 바랍니다.

다트용

///
/// Checks if these coordinates are valid geo coordinates.
/// [latitude]  The latitude must be in the range [-90, 90]
/// [longitude] The longitude must be in the range [-180, 180]
/// returns [true] if these are valid geo coordinates
///
bool coordinatesValid(double latitude, double longitude) {
  return (latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180);
}

///
/// Checks if the coordinates  of a GeopPoint are valid geo coordinates.
/// [latitude]  The latitude must be in the range [-90, 90]
/// [longitude] The longitude must be in the range [-180, 180]
/// returns [true] if these are valid geo coordinates
///
bool geoPointValid(GeoPoint point) {
  return (point.latitude >= -90 &&
      point.latitude <= 90 &&
      point.longitude >= -180 &&
      point.longitude <= 180);
}

///
/// Wraps the longitude to [-180,180].
///
/// [longitude] The longitude to wrap.
/// returns The resulting longitude.
///
double wrapLongitude(double longitude) {
  if (longitude <= 180 && longitude >= -180) {
    return longitude;
  }
  final adjusted = longitude + 180;
  if (adjusted > 0) {
    return (adjusted % 360) - 180;
  }
  // else
  return 180 - (-adjusted % 360);
}

double degreesToRadians(double degrees) {
  return (degrees * math.pi) / 180;
}

///
///Calculates the number of degrees a given distance is at a given latitude.
/// [distance] The distance to convert.
/// [latitude] The latitude at which to calculate.
/// returns the number of degrees the distance corresponds to.
double kilometersToLongitudeDegrees(double distance, double latitude) {
  const EARTH_EQ_RADIUS = 6378137.0;
  // this is a super, fancy magic number that the GeoFire lib can explain (maybe)
  const E2 = 0.00669447819799;
  const EPSILON = 1e-12;
  final radians = degreesToRadians(latitude);
  final numerator = math.cos(radians) * EARTH_EQ_RADIUS * math.pi / 180;
  final denom = 1 / math.sqrt(1 - E2 * math.sin(radians) * math.sin(radians));
  final deltaDeg = numerator * denom;
  if (deltaDeg < EPSILON) {
    return distance > 0 ? 360.0 : 0.0;
  }
  // else
  return math.min(360.0, distance / deltaDeg);
}

///
/// Defines the boundingbox for the query based
/// on its south-west and north-east corners
class GeoBoundingBox {
  final GeoPoint swCorner;
  final GeoPoint neCorner;

  GeoBoundingBox({this.swCorner, this.neCorner});
}

///
/// Defines the search area by a  circle [center] / [radiusInKilometers]
/// Based on the limitations of FireStore we can only search in rectangles
/// which means that from this definition a final search square is calculated
/// that contains the circle
class Area {
  final GeoPoint center;
  final double radiusInKilometers;

  Area(this.center, this.radiusInKilometers): 
  assert(geoPointValid(center)), assert(radiusInKilometers >= 0);

  factory Area.inMeters(GeoPoint gp, int radiusInMeters) {
    return new Area(gp, radiusInMeters / 1000.0);
  }

  factory Area.inMiles(GeoPoint gp, int radiusMiles) {
    return new Area(gp, radiusMiles * 1.60934);
  }

  /// returns the distance in km of [point] to center
  double distanceToCenter(GeoPoint point) {
    return distanceInKilometers(center, point);
  }
}

///
///Calculates the SW and NE corners of a bounding box around a center point for a given radius;
/// [area] with the center given as .latitude and .longitude
/// and the radius of the box (in kilometers)
GeoBoundingBox boundingBoxCoordinates(Area area) {
  const KM_PER_DEGREE_LATITUDE = 110.574;
  final latDegrees = area.radiusInKilometers / KM_PER_DEGREE_LATITUDE;
  final latitudeNorth = math.min(90.0, area.center.latitude + latDegrees);
  final latitudeSouth = math.max(-90.0, area.center.latitude - latDegrees);
  // calculate longitude based on current latitude
  final longDegsNorth = kilometersToLongitudeDegrees(area.radiusInKilometers, latitudeNorth);
  final longDegsSouth = kilometersToLongitudeDegrees(area.radiusInKilometers, latitudeSouth);
  final longDegs = math.max(longDegsNorth, longDegsSouth);
  return new GeoBoundingBox(
      swCorner: new GeoPoint(latitudeSouth, wrapLongitude(area.center.longitude - longDegs)),
      neCorner: new GeoPoint(latitudeNorth, wrapLongitude(area.center.longitude + longDegs)));
}

///
/// Calculates the distance, in kilometers, between two locations, via the
/// Haversine formula. Note that this is approximate due to the fact that
/// the Earth's radius varies between 6356.752 km and 6378.137 km.
/// [location1] The first location given
/// [location2] The second location given
/// sreturn the distance, in kilometers, between the two locations.
///
double distanceInKilometers(GeoPoint location1, GeoPoint location2) {
  const radius = 6371; // Earth's radius in kilometers
  final latDelta = degreesToRadians(location2.latitude - location1.latitude);
  final lonDelta = degreesToRadians(location2.longitude - location1.longitude);

  final a = (math.sin(latDelta / 2) * math.sin(latDelta / 2)) +
      (math.cos(degreesToRadians(location1.latitude)) *
          math.cos(degreesToRadians(location2.latitude)) *
          math.sin(lonDelta / 2) *
          math.sin(lonDelta / 2));

  final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));

  return radius * c;
}

저는 방금 https://pub.dartlang.org/packages/firestore_helpers 위의 JS 코드를 기반으로 Flutter 패키지를 게시했습니다.

예, 이것은 오래된 주제이지만, 저는 자바 코드에만 도움을 주고 싶습니다.경도 문제를 어떻게 해결했습니까?라이언 리와 마이클 테퍼의 코드를 사용했습니다.

A 코드:

@Override
public void getUsersForTwentyMiles() {
    FirebaseFirestore db = FirebaseFirestore.getInstance();

    double latitude = 33.0076665;
    double longitude = 35.1011336;

    int distance = 20;   //20 milles

    GeoPoint lg = new GeoPoint(latitude, longitude);

    // ~1 mile of lat and lon in degrees
    double lat = 0.0144927536231884;
    double lon = 0.0181818181818182;

    final double lowerLat = latitude - (lat * distance);
    final double lowerLon = longitude - (lon * distance);

    double greaterLat = latitude + (lat * distance);
    final double greaterLon = longitude + (lon * distance);

    final GeoPoint lesserGeopoint = new GeoPoint(lowerLat, lowerLon);
    final GeoPoint greaterGeopoint = new GeoPoint(greaterLat, greaterLon);

    Log.d(LOG_TAG, "local general lovation " + lg);
    Log.d(LOG_TAG, "local lesserGeopoint " + lesserGeopoint);
    Log.d(LOG_TAG, "local greaterGeopoint " + greaterGeopoint);

    //get users for twenty miles by only a latitude 
    db.collection("users")
            .whereGreaterThan("location", lesserGeopoint)
            .whereLessThan("location", greaterGeopoint)
            .get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        for (QueryDocumentSnapshot document : task.getResult()) {

                            UserData user = document.toObject(UserData.class);

                            //here a longitude condition (myLocation - 20 <= myLocation <= myLocation +20)
                            if (lowerLon <= user.getUserGeoPoint().getLongitude() && user.getUserGeoPoint().getLongitude() <= greaterLon) {
                                Log.d(LOG_TAG, "location: " + document.getId());
                            }                        
                        }  
                    } else {
                        Log.d(LOG_TAG, "Error getting documents: ", task.getException());
                    }
                }
            });
}

결과를 발행한 직후 필터를 경도로 설정합니다.

if (lowerLon <= user.getUserGeoPoint().getLongitude() && user.getUserGeoPoint().getLongitude() <= greaterLon) {
    Log.d(LOG_TAG, "location: " + document.getId());
}  

이것이 누군가에게 도움이 되기를 바랍니다.좋은 하루 보내세요!

GeoFire(Firestore와 함께 작동)를 사용해야 합니다.이렇게 하면 서버의 문서를 필터링하고 Firestore db에서 더 적은 수의 문서를 읽을 수 있습니다.이렇게 하면 읽기 횟수도 줄어듭니다.

이 lib에서 GroFire를 확인하십시오. https://github.com/patpatchpatrick/GeoFirestore-iOS

"패치패트릭"은 이것을 스위프트 5와 호환되도록 만들었습니다.

다음과 같이 포드 설치를 수행합니다.

pod 'Geofirestore', :git => 'https://github.com/patpatchpatrick/GeoFirestore-iOS'

제 프로젝트 중 하나에서 이 라이브러리를 사용하고 있는데 잘 작동합니다.

위치 설정하기

let location: CLLocation = CLLocation(latitude: lat, longitude: lng)
yourCollection.setLocation(location: location, forDocumentWithID: "YourDocId") { (error) in }

위치 제거하기

collection.removeLocation(forDocumentWithID: "YourDocId")

문서를 가져오는 방법:

let center = CLLocation(latitude: lat, longitude: lng)
let collection = "Your collection path"
let circleQuery = collection.query(withCenter: center, radius: Double(yourRadiusVal))
        
let _ = circleQuery.observe(.documentEntered, with: { (key, location) in
        //Use info as per your need
})

사용한 적이 있습니다..documentEntered필요에 따라 (문서 종료, 문서 이동)와 같은 다른 사용 가능한 지리적 쿼리를 사용할 수 있습니다.

GeoPoint를 사용하여 쿼리할 수도 있습니다.

이것은 아직 완전히 테스트되지 않았지만 Ryan Lee의 답변에 약간의 개선이 될 것입니다.

내 계산이 더 정확한 다음 경계 상자 안에 있지만 반경 밖에 있는 히트를 제거하기 위해 답을 필터링합니다.

스위프트 4

func getDocumentNearBy(latitude: Double, longitude: Double, meters: Double) {

    let myGeopoint = GeoPoint(latitude:latitude, longitude:longitude )
    let r_earth : Double = 6378137  // Radius of earth in Meters

    // 1 degree lat in m
    let kLat = (2 * Double.pi / 360) * r_earth
    let kLon = (2 * Double.pi / 360) * r_earth * __cospi(latitude/180.0)

    let deltaLat = meters / kLat
    let deltaLon = meters / kLon

    let swGeopoint = GeoPoint(latitude: latitude - deltaLat, longitude: longitude - deltaLon)
    let neGeopoint = GeoPoint(latitude: latitude + deltaLat, longitude: longitude + deltaLon)

    let docRef : CollectionReference = appDelegate.db.collection("restos")

    let query = docRef.whereField("location", isGreaterThan: swGeopoint).whereField("location", isLessThan: neGeopoint)
    query.getDocuments { snapshot, error in
      guard let snapshot = snapshot else {
        print("Error fetching snapshot results: \(error!)")
        return
      }
      self.documents = snapshot.documents.filter { (document)  in
        if let location = document.get("location") as? GeoPoint {
          let myDistance = self.distanceBetween(geoPoint1:myGeopoint,geoPoint2:location)
          print("myDistance:\(myDistance) distance:\(meters)")
          return myDistance <= meters
        }
        return false
      }
    }
  }

필터링을 위해 2개의 지오포인트 사이의 거리를 미터 단위로 정확하게 측정하는 기능

func distanceBetween(geoPoint1:GeoPoint, geoPoint2:GeoPoint) -> Double{
    return distanceBetween(lat1: geoPoint1.latitude,
                           lon1: geoPoint1.longitude,
                           lat2: geoPoint2.latitude,
                           lon2: geoPoint2.longitude)
}
func distanceBetween(lat1:Double, lon1:Double, lat2:Double, lon2:Double) -> Double{  // generally used geo measurement function
    let R : Double = 6378.137; // Radius of earth in KM
    let dLat = lat2 * Double.pi / 180 - lat1 * Double.pi / 180;
    let dLon = lon2 * Double.pi / 180 - lon1 * Double.pi / 180;
    let a = sin(dLat/2) * sin(dLat/2) +
      cos(lat1 * Double.pi / 180) * cos(lat2 * Double.pi / 180) *
      sin(dLon/2) * sin(dLon/2);
    let c = 2 * atan2(sqrt(a), sqrt(1-a));
    let d = R * c;
    return d * 1000; // meters
}

가장 쉬운 방법은 데이터베이스에 위치를 저장할 때 "geo 해시"를 계산하는 것입니다.

지오 해시는 특정 정확도까지 위치를 나타내는 문자열입니다.지오해시가 길수록 해당 지오해시가 있는 위치는 더 가까워져야 합니다.예를 들어 100m 떨어져 있는 두 위치는 6-chargeo 해시가 같을 수 있지만 7-chargeo 해시를 계산할 때 마지막 chargeo 해시가 다를 수 있습니다.

모든 언어에 대한 지오해시를 계산할 수 있는 많은 라이브러리가 있습니다.위치 옆에 저장하고 == 쿼리를 사용하여 동일한 지오 해시를 가진 위치를 찾기만 하면 됩니다.

자바스크립트에서 당신은 간단히 할 수 있습니다.

const db = firebase.firestore();

 //Geofire
import { GeoCollectionReference, GeoFirestore, GeoQuery, GeoQuerySnapshot } from 'geofirestore';

// Create a GeoFirestore reference
const geofirestore: GeoFirestore = new GeoFirestore(db);

// Create a GeoCollection reference
const geocollection: GeoCollectionReference = geofirestore.collection('<Your_collection_name>');

const query: GeoQuery = geocollectionDrivers.near({ 
        center: new firebase.firestore.GeoPoint(location.latitude, location.longitude), 
        radius: 10000 
    });
    query.onSnapshot(gquerySnapshot => {
        gquerySnapshot.forEach(res => {
            console.log(res.data());
        })
    });

Firestore에 lat/long을 기반으로 주문된 문서를 끌어오기 위한 네이티브 쿼리가 있을 때까지 Flutter에 대한 해결 방법: https://pub.dev/packages/geofutterfire 플러그인을 Firestore에 저장하고 쿼리합니다.

제한 사항: 제한이 지원되지 않습니다.

Geofirestore라는 Firestore용 GeoFire 라이브러리가 있습니다. https://github.com/imperiumlabs/GeoFirestore (Disclaimer:제가 개발을 도왔습니다.매우 사용하기 쉽고 Geofire가 Firebase Realtime DB에 제공하는 것과 동일한 기능을 Firestore에 제공합니다.)

언급URL : https://stackoverflow.com/questions/46630507/how-to-run-a-geo-nearby-query-with-firestore

반응형