(Đầu tiên hãy để tôi xin lỗi vì tất cả mã trong bài đăng này, tôi chỉ muốn mọi người đọc câu trả lời này để có một thời gian dễ dàng sao chép lại chức năng.)
Để giải quyết mối quan tâm tương tự với OP, ban đầu tôi đã điều chỉnh GeoFire library để làm việc với Firestore (bạn có thể tìm hiểu rất nhiều về công cụ địa lý bằng cách xem thư viện đó). Sau đó, tôi nhận ra tôi đã không thực sự tâm trí nếu địa điểm được trả lại trong một vòng tròn chính xác. Tôi chỉ muốn một số cách để có được vị trí 'lân cận'. Tôi không thể tin rằng tôi mất bao lâu để nhận ra điều này, nhưng bạn có thể thực hiện truy vấn bất bình đẳng kép trên trường GeoPoint bằng góc SW và góc NE để lấy vị trí trong một hộp giới hạn quanh điểm trung tâm.
Vì vậy, tôi đã thực hiện một chức năng JavaScript như dưới đây (đây là cơ bản một phiên bản JS của câu trả lời của Ryan Lee).
/**
* 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');
});
}
Chức năng trên cũng cho biết thêm một tài sản .distanceFromCenter cho mỗi mảnh của dữ liệu vị trí đó là quay trở lại để bạn có thể nhận được các hành vi hình tròn giống như bởi chỉ kiểm tra nếu khoảng cách đó là trong phạm vi mà bạn muốn.
Tôi sử dụng hai hàm util trong hàm ở trên, vì vậy, đây cũng là mã cho các hàm đó. (Tất cả các hàm util bên dưới đều thực sự được điều chỉnh từ thư viện GeoFire.)
khoảng cách():
/**
* 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;
}
boundingBoxCoordinates(): (Có nhiều utils sử dụng trong đây cũng là tôi đã dán bên dưới)
/**
* 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),
},
};
}
metersToLongitudeDegrees():.
/**
* 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);
}
wrapLongitude():
/**
* 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);
}
Bản sao có thể có của [Cách truy vấn GeoPoints gần nhất trong bộ sưu tập trong Firebase Cloud Firestore?] (Https://stackoverflow.com/questions/46607760/how-to-query-closest-geopoints-in-a-collection -in-firebase-cloud-firestore) –