如何从地理查询中获取文档?

How to get Documents out of an geo query?

我将此函数用于地理查询。但我不知道如何将查询中的文档添加到数组中。因此,我可以显示一些带有来自 Firestore 文档的信息的地图注释。我该如何更改?

    func geoQuery() {
        // [START fs_geo_query_hashes]
        // Find cities within 50km of London
        let center = CLLocationCoordinate2D(latitude: 51.5074, longitude: 0.1278)
        let radiusInKilometers: Double = 50

        // Each item in 'bounds' represents a startAt/endAt pair. We have to issue
        // a separate query for each pair. There can be up to 9 pairs of bounds
        // depending on overlap, but in most cases there are 4.
        let queryBounds = GFUtils.queryBounds(forLocation: center,
                                              withRadius: radiusInKilometers)
        let queries = queryBounds.compactMap { (any) -> Query? in
            guard let bound = any as? GFGeoQueryBounds else { return nil }
            return db.collection("cities")
                .order(by: "geohash")
                .start(at: [bound.startValue])
                .end(at: [bound.endValue])
        }

        var matchingDocs = [QueryDocumentSnapshot]()
        // Collect all the query results together into a single list
        func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
            guard let documents = snapshot?.documents else {
                print("Unable to fetch snapshot data. \(String(describing: error))")
                return
            }

            for document in documents {
                let lat = document.data()["lat"] as? Double ?? 0
                let lng = document.data()["lng"] as? Double ?? 0
                let coordinates = CLLocation(latitude: lat, longitude: lng)
                let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude)

                // We have to filter out a few false positives due to GeoHash accuracy, but
                // most will match
                let distance = GFUtils.distance(from: centerPoint, to: coordinates)
                if distance <= radiusInKilometers {
                    matchingDocs.append(document)
                }
            }
        }

        // After all callbacks have executed, matchingDocs contains the result. Note that this
        // sample does not demonstrate how to wait on all callbacks to complete.
        for query in queries {
            query.getDocuments(completion: getDocumentsCompletion)
        }
        // [END fs_geo_query_hashes]
    }

https://firebase.google.com/docs/firestore/solutions/geoqueries?hl=en#swift_2这是 Firebase 纪录片。

这是我在结构中的 Pin 模型:

struct Pin: Identifiable {
    var id = UUID().uuidString
    var location: MKCoordinateRegion
    var name: String
    var img: String
}

然后我会使用地理查询功能:

func geoQuery() {
        // [START fs_geo_query_hashes]
        // Find cities within 50km of London
        let center = CLLocationCoordinate2D(latitude: 51.5074, longitude: 0.1278)
        let radiusInKilometers: Double = 50

        // Each item in 'bounds' represents a startAt/endAt pair. We have to issue
        // a separate query for each pair. There can be up to 9 pairs of bounds
        // depending on overlap, but in most cases there are 4.
        let queryBounds = GFUtils.queryBounds(forLocation: center,
                                              withRadius: radiusInKilometers)
        let queries = queryBounds.compactMap { (any) -> Query? in
            guard let bound = any as? GFGeoQueryBounds else { return nil }
            return db.collection("cities")
                .order(by: "geohash")
                .start(at: [bound.startValue])
                .end(at: [bound.endValue])
        }

        var matchingDocs = [QueryDocumentSnapshot]()
        // Collect all the query results together into a single list
        func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
            guard let documents = snapshot?.documents else {
                print("Unable to fetch snapshot data. \(String(describing: error))")
                return
            }

            for document in documents {
                let lat = document.data()["lat"] as? Double ?? 0
                let lng = document.data()["lng"] as? Double ?? 0
                let name = document.data()["names"] as? String ?? "no name"
                let coordinates = CLLocation(latitude: lat, longitude: lng)
                let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude)

                // We have to filter out a few false positives due to GeoHash accuracy, but
                // most will match
                let distance = GFUtils.distance(from: centerPoint, to: coordinates)
                if distance <= radiusInKilometers {
                    matchingDocs.append(document)
                }
            }
        }

        // After all callbacks have executed, matchingDocs contains the result. Note that this
        // sample does not demonstrate how to wait on all callbacks to complete.
        for query in queries {
            query.getDocuments(completion: getDocumentsCompletion)
        }
        // [END fs_geo_query_hashes]
    }

但现在我卡住了如何将它添加到数组中。

我不知道您的文档是如何构建的,也不知道您的地图是如何配置以显示数据的(例如,注释与区域),但解决您的问题的一般方法是协调函数中的查询循环并给他们一个完成处理程序。为此,我们可以使用 Dispatch Group。在该组的完成处理程序中,您有一个文档快照数组,您需要循环获取数据(从每个文档),构建 Pin,并将其添加到地图中。这里涉及许多其他步骤,我无法帮助您,因为我不知道您的文档和地图是如何配置的,但这会对您有所帮助。也就是说,您可以稍微减少此代码并使其更高效,但让我们先使用您正在使用的 Firebase 示例代码并使其运行。

struct Pin: Identifiable {
    let id = UUID().uuidString
    var location: MKCoordinateRegion
    var name: String
    var img: String
}

func geoQuery() {
    // [START fs_geo_query_hashes]
    // Find cities within 50km of London
    let center = CLLocationCoordinate2D(latitude: 51.5074, longitude: 0.1278)
    let radiusInKilometers: Double = 50
    
    // Each item in 'bounds' represents a startAt/endAt pair. We have to issue
    // a separate query for each pair. There can be up to 9 pairs of bounds
    // depending on overlap, but in most cases there are 4.
    let queryBounds = GFUtils.queryBounds(forLocation: center,
                                          withRadius: radiusInKilometers)
    let queries = queryBounds.compactMap { (Any) -> Query? in
        guard let bound = Any as? GFGeoQueryBounds else { return nil }
        return db.collection("cities")
            .order(by: "geohash")
            .start(at: [bound.startValue])
            .end(at: [bound.endValue])
    }
    
    // Create a dispatch group outside of the query loop since each iteration of the loop
    // performs an asynchronous task.
    let dispatch = DispatchGroup()
    
    var matchingDocs = [QueryDocumentSnapshot]()
    // Collect all the query results together into a single list
    func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
        guard let documents = snapshot?.documents else {
            print("Unable to fetch snapshot data. \(String(describing: error))")
            dispatch.leave() // leave the dispatch group when we exit this completion
            return
        }
        
        for document in documents {
            let lat = document.data()["lat"] as? Double ?? 0
            let lng = document.data()["lng"] as? Double ?? 0
            let name = document.data()["names"] as? String ?? "no name"
            let coordinates = CLLocation(latitude: lat, longitude: lng)
            let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude)
            
            // We have to filter out a few false positives due to GeoHash accuracy, but
            // most will match
            let distance = GFUtils.distance(from: centerPoint, to: coordinates)
            if distance <= radiusInKilometers {
                matchingDocs.append(document)
            }
        }
        dispatch.leave() // leave the dispatch group when we exit this completion
    }
    
    // After all callbacks have executed, matchingDocs contains the result. Note that this
    // sample does not demonstrate how to wait on all callbacks to complete.
    for query in queries {
        dispatch.enter() // enter the dispatch group on each iteration
        query.getDocuments(completion: getDocumentsCompletion)
    }
    // [END fs_geo_query_hashes]
    
    // This is the completion handler of the dispatch group. When all of the leave()
    // calls equal the number of enter() calls, this notify function is called.
    dispatch.notify(queue: .main) {
        for doc in matchingDocs {
            let lat = doc.data()["lat"] as? Double ?? 0
            let lng = doc.data()["lng"] as? Double ?? 0
            let name = doc.data()["names"] as? String ?? "no name"
            let coordinates = CLLocation(latitude: lat, longitude: lng)
            let region = MKCoordinateRegion(center: <#T##CLLocationCoordinate2D#>, latitudinalMeters: <#T##CLLocationDistance#>, longitudinalMeters: <#T##CLLocationDistance#>)
            let pin = Pin(location: region, name: name, img: "someImg")
            
            // Add pin to array and then to map or just add pin directly to map here.
        }
    }
}
        Map(coordinateRegion: $region, interactionModes: .all, showsUserLocation: true, annotationItems: pvm.allPins) { pin in
                MapAnnotation(coordinate: pin.location.coordinate) {
                    Image(pin.img)
                }
                
            }