iOS Swift中如何计算Firestore两个不同集合的经纬度距离
在iOS Swift中计算Firestore两个集合的经纬度距离
首先得明确两个核心环节:从Firestore拉取对应数据,以及计算两组经纬度间的实际距离。下面我一步步给你拆解实现逻辑:
1. 导入必要框架
先确保你已经集成了Firebase和CoreLocation(用系统自带的距离计算API会更省心),在Swift文件顶部添加:
import FirebaseFirestore import CoreLocation
2. 获取Current_booking的起点经纬度
首先得拿到订单的起点坐标,假设你知道目标订单的文档ID,或者需要查询特定条件的订单:
func fetchBookingStartLocation(bookingDocID: String, completion: @escaping (CLLocationCoordinate2D?) -> Void) { let db = Firestore.firestore() db.collection("Current_booking").document(bookingDocID).getDocument { snapshot, error in guard let doc = snapshot, doc.exists, let startLat = doc["Start_lat"] as? Double, let startLong = doc["Start_long"] as? Double else { print("获取起点失败:\(error?.localizedDescription ?? "未知错误")") completion(nil) return } let startCoord = CLLocationCoordinate2D(latitude: startLat, longitude: startLong) completion(startCoord) } }
3. 获取目标区域司机数据并计算距离
根据业务需求(比如用户所在的城市、街道区域),从Driver_details的层级结构中拉取司机坐标,再逐个计算到起点的距离:
func calculateDriversToStartDistance(startCoord: CLLocationCoordinate2D, city: String, area: String) { let db = Firestore.firestore() // 对应Driver_details -> 城市 -> 街道区域的集合路径 let driversCollection = db.collection("Driver_details").document(city).collection(area) driversCollection.getDocuments { snapshot, error in guard let driverDocs = snapshot?.documents, error == nil else { print("获取司机数据失败:\(error?.localizedDescription ?? "未知错误")") return } let startLocation = CLLocation(latitude: startCoord.latitude, longitude: startCoord.longitude) for driverDoc in driverDocs { guard let driverLat = driverDoc["lat"] as? Double, let driverLong = driverDoc["long"] as? Double, let driverName = driverDoc["name"] as? String else { continue } let driverLocation = CLLocation(latitude: driverLat, longitude: driverLong) // 计算距离,单位是米,转公里的话除以1000即可 let distanceInMeters = startLocation.distance(from: driverLocation) let distanceInKm = distanceInMeters / 1000 print("司机\(driverName)到起点的距离:\(String(format: "%.2f", distanceInKm))公里") } } }
4. 组合调用流程
把上面两个方法结合起来,处理异步数据的加载顺序:
func startDistanceCalculation(targetCity: String, targetArea: String, targetBookingID: String) { fetchBookingStartLocation(bookingDocID: targetBookingID) { startCoord in guard let coord = startCoord else { return } self.calculateDriversToStartDistance(startCoord: coord, city: targetCity, area: targetArea) } } // 调用示例:查询班加罗尔st.thomas区域的司机到某订单的距离 startDistanceCalculation(targetCity: "班加罗尔", targetArea: "st.thomas", targetBookingID: "YOUR_BOOKING_DOC_ID")
备选:手动实现Haversine公式(无需CoreLocation)
如果不想依赖CoreLocation框架,可以自己实现球面距离计算的Haversine公式:
func haversineDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double { let earthRadiusKm = 6371.0 // 地球平均半径(公里) let dLat = (lat2 - lat1).toRadians() let dLon = (lon2 - lon1).toRadians() let a = sin(dLat/2) * sin(dLat/2) + cos(lat1.toRadians()) * cos(lat2.toRadians()) * sin(dLon/2) * sin(dLon/2) let c = 2 * atan2(sqrt(a), sqrt(1 - a)) return earthRadiusKm * c } // 给Double添加弧度转换扩展 extension Double { func toRadians() -> Double { return self * .pi / 180.0 } }
使用时替换之前的距离计算代码即可:
let distance = haversineDistance(lat1: startCoord.latitude, lon1: startCoord.longitude, lat2: driverLat, lon2: driverLong) print("司机\(driverName)到起点的距离:\(String(format: "%.2f", distance))公里")
关键注意事项
- Firestore查询是异步的,务必用completion handler或Swift 5.5+的
async/await处理数据加载顺序,避免空值问题。 - 如果司机数据量很大,建议在Firestore端做地理过滤查询(比如只拉取起点周边X公里内的司机),而不是全量拉取到客户端计算——Firestore支持基于
GeoPoint的地理空间查询,能大幅提升性能和节省带宽。 - 确认Firestore安全规则允许读取这两个集合,避免因权限问题导致数据拉取失败。
内容的提问来源于stack exchange,提问作者PvDev
相关产品推荐
相关产品推荐

