【问题标题】:Data from GeoJSON URL end point not appearing on Map using MKAnnotations来自 GeoJSON URL 端点的数据未使用注释出现在地图上
【发布时间】:2021-04-11 00:15:18
【问题描述】:

我正在创建我的第一个 IOS 应用程序,但我不是开发人员,我真的被 Map Annotations 困住了。

我正在尝试从 GeoJSON URL 端点获取 Fire 数据,并使用 URLSession 和自定义 MKAnnotationView 和自定义 fire Pins 将火灾显示为地图上的注释。

问题是来自 URL 端点的 GeoJSON Fire 数据的注释没有出现在地图上,尽管 URL 会话正在返回数据。但是,如果我手动创建单个 Fire 注释,它会使用自定义图钉正确显示在地图上。

任何帮助都将不胜感激,我花了几天时间试图解决这个问题:(

这是 ViewController.Swift 文件

import UIKit
import MapKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {

    @IBOutlet weak var mapView: MKMapView!
    var locationManager:CLLocationManager!
    var lat = Double()
    var lon = Double()
    var fires: [Fire] = []
    
    override func viewDidLoad() {

        super.viewDidLoad()
     
        mapView.delegate = self
        mapView.register(FireMarkerView.self,forAnnotationViewWithReuseIdentifier:
            MKMapViewDefaultAnnotationViewReuseIdentifier)
          
        if let url = URL(string: "https://services3.arcgis.com/T4QMspbfLg3qTGWY/arcgis/rest/services/Active_Fires/FeatureServer/0/query?outFields=*&where=1%3D1&f=geojson") {
              
            URLSession.shared.dataTask(with: url) {data, response, error in
                    if let data = data {
                       do {
                           let features = try MKGeoJSONDecoder().decode(data)
                               .compactMap { $0 as? MKGeoJSONFeature }
                             let validWorks = features.compactMap(Fire.init)
                        self.fires.append(contentsOf: validWorks)
                        print([self.fires])
                        
                                 }
                       catch let error {
                                    print(error)
                                   
                       }
                     }
                 }.resume()
           }
        
        
//This code works an annotation appears correctly on map
        /* let fire = Fire(
        title: "Ford Fire",
        incidentShortDescription: "Hwy 35",
        incidentTypeCategory: "WF",
        coordinate: CLLocationCoordinate2D(latitude: 37.7993, longitude: -122.1947))
       
        mapView.addAnnotation(fire)*/
        
        mapView.addAnnotations(fires)
    }
        
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        determineMyCurrentLocation()
    }
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {

    switch manager.authorizationStatus {
        case .authorizedAlways , .authorizedWhenInUse:
            mapView.showsUserLocation = true
            followUserLocation()
            locationManager.startUpdatingLocation()
            break
        case .notDetermined , .denied , .restricted:
            locationManager.requestWhenInUseAuthorization()
            break
        default:
            break
    }
    
    switch manager.accuracyAuthorization {
        case .fullAccuracy:
            break
        case .reducedAccuracy:
            break
        default:
            break
    }
}
func followUserLocation() {
    if let location = locationManager.location?.coordinate {
        let region = MKCoordinateRegion.init(center: location, latitudinalMeters: 4000, longitudinalMeters: 4000)
        mapView.setRegion(region, animated: true)
    }
}

    func determineMyCurrentLocation() {
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        
        if CLLocationManager.locationServicesEnabled() {
            locationManager.startUpdatingLocation()
            //locationManager.startUpdatingHeading()
        }
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        
        let userLocation = locations.first! as CLLocation
        lat = userLocation.coordinate.latitude
        lon = userLocation.coordinate.longitude
        
        let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: 400000, longitudinalMeters: 400000)
        self.mapView.setRegion(region, animated: true)

        // Call stopUpdatingLocation() to stop listening for location updates,
        // other wise this function will be called every time when user location changes.
        // Need a solution for this.
                manager.stopUpdatingLocation()
        
        print("user latitude = \(userLocation.coordinate.latitude)")
        print("user longitude = \(userLocation.coordinate.longitude)")
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
    {
        print("Error \(error)")
    }
}

这里是模型类,Fire.swift

import Foundation
import MapKit

class Fire: NSObject, MKAnnotation {
  let title: String?
  let incidentShortDescription: String?
  let incidentTypeCategory: String?
  let coordinate: CLLocationCoordinate2D

  init(
    title: String?,
    incidentShortDescription: String?,
    incidentTypeCategory: String?,
    coordinate: CLLocationCoordinate2D
  ) {
    self.title = title
    self.incidentShortDescription = incidentShortDescription
    self.incidentTypeCategory = incidentTypeCategory
    self.coordinate = coordinate

    super.init()
  }




init?(feature: MKGeoJSONFeature) {
  // 1
  guard
    let point = feature.geometry.first as? MKPointAnnotation,
    let propertiesData = feature.properties,
    let json = try? JSONSerialization.jsonObject(with: propertiesData),
    let properties = json as? [String: Any]
    else {
      return nil
  }

  // 3
    
  title = properties ["IncidentName"] as? String
  incidentShortDescription = properties["IncidentShortDescription"] as? String
  incidentTypeCategory = properties["IncidentTypeCategory"] as? String
  coordinate = point.coordinate
  super.init()
}
    var subtitle: String? {
        return (incidentTypeCategory)
    }
    
    
    var image: UIImage {
      guard let name = incidentTypeCategory else {
        return #imageLiteral(resourceName: "RedFlame")
      }

      switch name {
      case "RX":
        return #imageLiteral(resourceName: "YellowFlame")
      default:
        return #imageLiteral(resourceName: "RedFlame")
      }
    }

这是自定义的 MKAnnotation 类:FileMarkerView.swift

import Foundation
import MapKit

class FireMarkerView: MKAnnotationView {
  override var annotation: MKAnnotation? {
    willSet {
      guard let fire = newValue as? Fire else {
        return
      }

      canShowCallout = true
      calloutOffset = CGPoint(x: -5, y: 5)
      rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

      image = fire.image
    }
  }
}

【问题讨论】:

    标签: ios swift geojson mkannotation urlsession


    【解决方案1】:

    URLSession.shared.dataTask 是一个异步 任务,这意味着它会在未来某个不确定的时间调用它的回调函数。在其回调({ }之外执行的代码最终会在数据任务实际完成之前被调用。现在,您正在该回调之外设置注释。

    要解决这个问题,您需要在该回调函数的inside 中设置注释。所以,如果你有print([self.fires]),你可以这样做:

    DispatchQueue.main.async { 
      self.mapView.addAnnotations(self.fires)
    } 
    

    DispatchQueue.main.async 是为了确保在主线程上调用 UI 更新(URL 任务可能在不同的线程上返回)。

    【讨论】:

      猜你喜欢
      • 2014-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多