Mon*_*key 53 firebase google-cloud-firestore
来自firebase的新firestore数据库本身是否支持基于位置的地理查询?即查找10英里内的帖子,或找到最近的50个帖子?
我看到实时firebase数据库有一些现有的项目,像geofire这样的项目 - 那些也可以适应firestore吗?
Rya*_*Lee 39
更新:Firestore目前不支持实际的GeoPoint查询,因此当下面的查询成功执行时,它只按纬度而不是经度进行过滤,因此会返回许多不在附近的结果.最好的解决方案是使用geohashes.要了解如何自己做类似的事情,请看一下这个视频.
这可以通过创建小于大于查询的边界框来完成.至于效率,我不能说话.
注意,应该检查~1英里的纬度/经度偏移的精度,但是这里有一个快速的方法:
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)
}
Run Code Online (Sandbox Code Playgroud)
stp*_*ham 22
更新:Firestore目前不支持实际的GeoPoint查询,因此当下面的查询成功执行时,它只按纬度而不是经度进行过滤,因此会返回许多不在附近的结果.最好的解决方案是使用geohashes.要了解如何自己做类似的事情,请看一下这个视频.
(首先让我为这篇文章中的所有代码道歉,我只是希望任何阅读此答案的人都可以轻松地重现功能.)
为了解决OP所面临的同样问题,我首先调整了GeoFire库以与Firestore一起工作(通过查看该库可以了解很多关于地理信息的内容).然后我意识到我并不介意如果位置以精确的圆圈返回.我只是想通过某种方式获得"附近"的位置.
我无法相信我花了多长时间才意识到这一点,但您可以使用SW角和NE角在GeoPoint场上执行双重不等式查询,以获取围绕中心点的边界框内的位置.
所以我创建了一个类似下面的JavaScript函数(这基本上是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');
});
}
Run Code Online (Sandbox Code Playgroud)
上面的函数还为返回的每个位置数据添加.distanceFromCenter属性,这样您只需检查该距离是否在您想要的范围内,就可以获得类似圆的行为.
我在上面的函数中使用了两个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;
}
Run Code Online (Sandbox Code Playgroud)
boundingBoxCoordinates():(此处还有更多的utils,我在下面粘贴过.)
/**
* 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),
},
};
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
小智 11
截至今天,没有办法做这样的查询.SO中还有其他与之相关的问题:
如何在Firebase Cloud Firestore中查询集合中最近的GeoPoints?
在我目前的Android项目中,我可以使用https://github.com/drfonfon/android-geohash添加geohash字段,而Firebase团队正在开发本机支持.
使用其他问题中建议的Firebase实时数据库意味着您无法同时按位置和其他字段过滤结果集,这是我想要首先切换到Firestore的主要原因.
自@monkeybonkey首先提出这个问题以来,已经引入了一个新项目.该项目名为GEOFirestore.
使用此库,您可以执行查询,例如圆圈内的查询文档:
const geoQuery = geoFirestore.query({
center: new firebase.firestore.GeoPoint(10.38, 2.41),
radius: 10.5
});
Run Code Online (Sandbox Code Playgroud)
您可以通过npm安装GeoFirestore.您必须单独安装Firebase(因为它是GeoFirestore的对等依赖项):
$ npm install geofirestore firebase --save
Run Code Online (Sandbox Code Playgroud)
小智 6
劫持此线程希望能帮助任何仍在寻找的人。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();
Run Code Online (Sandbox Code Playgroud)
repo 上有更多文档。它对我有用,所以试一试,希望它能满足你的需求。
截至 2020 年底,现在还有关于如何使用 Firestore 进行地理查询的文档。
这些适用于 iOS、Android 和 Web 的解决方案构建在 Firebase 创建的 GeoFire 库的精简版之上,然后展示了如何:
这比此处介绍的大多数其他库要低级一些,因此它可能更适合某些用例,而不太适合其他用例。
| 归档时间: |
|
| 查看次数: |
28379 次 |
| 最近记录: |