0

我在我的 Swift 项目中使用 Mapbox SDK,并为一些火车路径添加了自定义轨道。暂时,我正在办公室周围测试应用程序,因此,我创建了办公室周围的自定义路线。

这些自定义路线不是 Mapbox 或 Google 地图中存在的可路由轨道或道路。

我必须只在我的自定义轨道上显示任何 GPS 位置,而不是偏离轨道。

让我们以代码为例,我有以下代码,它显示了我所在的 GPS 位置。

func setAnnotation(atLocation location: CLLocation, forDevice device: String, andUser userId: Int) {

    var locationMarker = LSTPointAnnotation()
    
    // Check from list of annotations if the annotation exists or not 
    // as I am showing multiple annotations at a time for different people
    // and all annotations update their individual locations in async fashion
    if let userMarker = self.markerAnnotations[device] {
        locationMarker = userMarker
    } else {
        markerAnnotations[device] = locationMarker
        userDevices[userId] = device
        self.mapView?.addAnnotation(locationMarker)
    }

    // We are creating annotation from Mapbox's 'viewForAnnotation' method
    if let annotationView = self.mapView?.view(for: locationMarker) as? LSTLocoAnnotationView {
        // I am changing color of annotation view after some processing
        // Here only UI customisation is done
    }

    // Updating location of the annotation
    locationMarker.coordinate = location.coordinate

}

基本上上述函数的作用是,

  • 从可用注释标记列表中获取用户标记或创建新注释
  • 使用我们拥有的新位置更新该标记的位置location

上面的这个功能工作得很好,我的位置正在更新,说明我站在哪里。

以下是此功能的结果,您可以在其中看到我站立的位置(偏离路线),

在此处输入图像描述

在这里,我的位置将在我移动时不断更新,并精确显示我的位置,有时在轨道上,但大部分时间不在轨道上。

但是,我的目标不是始终显示偏离轨道的位置,而是显示在轨道上的位置。

以下是我添加的用于显示我在赛道上的位置的代码,但这有点吓人:(。

我们在setAnnotation这里更新相同的功能,

func setAnnotation(atLocation location: CLLocation, forDevice device: String, andUser userId: Int) {

    var locationMarker = LSTPointAnnotation()
    
    // Check from list of annotations if the annotation exists or not 
    // as I am showing multiple annotations at a time for different people
    // and all annotations update their individual locations in async fashion
    if let userMarker = self.markerAnnotations[device] {
        locationMarker = userMarker
    } else {
        markerAnnotations[device] = locationMarker
        userDevices[userId] = device
        self.mapView?.addAnnotation(locationMarker)
    }

    // Now I have geoJson file for custom route 
    // as I have stylised that route with dash lines
    // Accessing all features with dash-lines
    let features = self.mapView?.visibleFeatures(in: map.bounds, styleLayerIdentifiers: Set(arrayLiteral: "polyline-dash"))

    // Will mark nearest-coordinates and nearest distance with new location of that coordinate
    var nearestCoord: CLLocationCoordinate2D?
    var nearest: Double?

    // Sorting all feature points based on nearest location distance (Assumption)
    if let json = features?.sorted(by: { firstFeature, secondFeature in
        let firstDistance = firstFeature.coordinate.distance(to: location.coordinate)
        let secondDistance = secondFeature.coordinate.distance(to: location.coordinate)
        
        return firstDistance < secondDistance
    }).first?.geoJSONDictionary(),
       let geometry = json["geometry"] as? Dictionary<String, Any>,
       let coords = geometry["coordinates"] as? Array<Array<Any>> {

            // Have to take geoJSONDictionary as without it
            // I would not be able to access underlying coordinates of the feature
            // feature.coordinate gives centre of Mapbox View (It always keep annotation at centre of the screen which is not required)

        // Loop through all coordinates and finding nearest coord with new location
        for coord in coords {
            if let inners = coord as? Array<Any> {
                for inner in inners {
                    guard let t1 = inner as? Array<Double> else {   return   }
                    let c1 = CLLocationCoordinate2D(latitude: t1[1], longitude: t1[0])
                    
                    let firstDistance = c1.distance(to: location.coordinate)
                    
                    // Save nearest location and nearest distance
                    if let dist = nearest {
                        if firstDistance < dist {
                            nearest = firstDistance
                            nearestCoord = c1
                        }
                    } else {
                        nearest = firstDistance
                        nearestCoord = c1
                    }
                }
            }
        }

    }

    // We are creating annotation from Mapbox's 'viewForAnnotation' method
    if let annotationView = self.mapView?.view(for: locationMarker) as? LSTLocoAnnotationView {
        // I am changing color of annotation view after some processing
        // Here only UI customisation is done
    }

    // Updating location of the annotation with nearest coordinate from route
    // But if distance is too large then we are showing original location
    if let near = nearestCoord, (nearest ?? 0) <= 100 {
        locationMarker.coordinate = near
    } else {
        locationMarker.coordinate = location.coordinate
    }

}

上面的函数所做的是,

  • 从 geoJson 获取特征点
  • 过滤所有坐标并通过检查与新位置的距离来找到最近的坐标
  • 找到最近坐标后,在始终在轨道上的最近坐标上显示注释,或者如果距离太远,则在原始位置显示注释

这基本上显示了我在赛道上的位置,但我还没有测试整个赛道(我很快就会这样做)。

上述函数的结果,它可能会给出断断续续的响应,

在此处输入图像描述

但是,我想重构这个函数,并且如果我能够从 Mapbox 的样式 URL 中提取 geoJSON 文件,我还想要一些指针(我已经在 Mapbox 地图中添加了样式)。

4

0 回答 0