如何为Firestore QuerySnapshot添加计算距离字段并按距离排序
问题描述
从Firestore获取QuerySnapshot后,将文档列表赋值给userDocs:
List userDocs = snapShot.data!.docs;
userDocs中的每个文档包含GeoPoint类型的位置字段,需要计算当前用户位置与每个文档中GeoPoint的距离,把计算出的距离作为新字段添加到每个文档项中,再按距离从近到远排序。
已尝试的代码:
List userDocs = snapShot.data!.docs; String distKm =""; // 遍历计算到点位的距离 int i = 0; userDocs.forEach((e) { print("loop ${i}"); print(e['spot_id']); // 计算距离 GeoPoint geoPoint = e['location']; var latPost = geoPoint.latitude; var lonPost = geoPoint.longitude; var distancia = calculateDistance( widget.latitud, widget.longitud, latPost, lonPost); print("distancia ${distancia}"); distKm = distancia.toStringAsFixed(2); print("distkm ${distKm}"); // 更新userDocs中的项 i++; });
补充:距离计算函数
double calculateDistance(lat1, lon1, lat2, lon2) { var p = 0.017453292519943295; var c = cos; var a = 0.5 - c((lat2 - lat1) * p) / 2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p)) / 2; return 12742 * asin(sqrt(a)); }
解决方案
方法1:转为可修改Map添加字段
Firestore的DocumentSnapshot是不可变对象,无法直接添加字段,需先将每个文档转为可修改的Map,再扩展距离字段:
List<Map<String, dynamic>> userDocsWithDistance = []; userDocs.forEach((doc) { // 将文档数据转为可修改Map Map<String, dynamic> docData = Map.from(doc.data() as Map<String, dynamic>); GeoPoint geoPoint = docData['location']; double latPost = geoPoint.latitude; double lonPost = geoPoint.longitude; // 计算距离 double distance = calculateDistance( widget.latitud, widget.longitud, latPost, lonPost); // 添加数值型距离(用于排序)和格式化字符串(用于展示) docData['distance_km'] = distance; docData['distance_display'] = distance.toStringAsFixed(2); userDocsWithDistance.add(docData); }); // 按距离从小到大排序 userDocsWithDistance.sort((a, b) => a['distance_km'].compareTo(b['distance_km']));
方法2:用自定义类封装数据(更规范)
如果需要频繁处理这类数据,建议创建自定义类封装文档信息和距离:
class UserSpot { final String spotId; final GeoPoint location; final double distanceKm; UserSpot({ required this.spotId, required this.location, required this.distanceKm, }); // 从DocumentSnapshot和计算出的距离生成实例 factory UserSpot.fromDoc(DocumentSnapshot doc, double distance) { return UserSpot( spotId: doc['spot_id'], location: doc['location'], distanceKm: distance, ); } } // 使用示例 List<UserSpot> userSpots = []; userDocs.forEach((doc) { GeoPoint geoPoint = doc['location']; double distance = calculateDistance( widget.latitud, widget.longitud, geoPoint.latitude, geoPoint.longitude); userSpots.add(UserSpot.fromDoc(doc, distance)); }); // 按距离排序 userSpots.sort((a, b) => a.distanceKm.compareTo(b.distanceKm));
重要提示
- 禁止直接修改
DocumentSnapshot对象,必须转为Map或自定义类扩展字段 - 排序必须使用数值类型的距离(
double),不能用格式化后的字符串,否则会导致排序逻辑错误(例如字符串"10.00"会被判定为小于"2.00")
内容的提问来源于stack exchange,提问作者mvasco
相关产品推荐
相关产品推荐

