【问题标题】:Convert coordinates to City name?将坐标转换为城市名称?
【发布时间】:2015-01-01 23:52:59
【问题描述】:

如何使用 MapKit 从坐标中获取地址?

我有这个代码,当长按地图时它会得到坐标:

func didLongPressMap(sender: UILongPressGestureRecognizer) {

    if sender.state == UIGestureRecognizerState.Began {
        let touchPoint = sender.locationInView(self.mapView)
        let touchCoordinate = self.mapView.convertPoint(touchPoint, toCoordinateFromView: self.mapView)
        var annotation = MKPointAnnotation()
        annotation.coordinate = touchCoordinate
        annotation.title = "Your position"
        self.mapView.addAnnotation(annotation) //drops the pin
        println("lat:  \(touchCoordinate.latitude)")
        var num = (touchCoordinate.latitude as NSNumber).floatValue
        var formatter = NSNumberFormatter()
        formatter.maximumFractionDigits = 4
        formatter.minimumFractionDigits = 4
        var str = formatter.stringFromNumber(num)
        println("long: \(touchCoordinate.longitude)")
        var num1 = (touchCoordinate.longitude as NSNumber).floatValue
        var formatter1 = NSNumberFormatter()
        formatter1.maximumFractionDigits = 4
        formatter1.minimumFractionDigits = 4
        var str1 = formatter1.stringFromNumber(num1)
        self.adressLoLa.text = "\(num),\(num1)"
                }
}

我想在annotation.title 中打印完整地址(街道、城市、邮编、国家/地区)。

【问题讨论】:

    标签: ios swift mapkit clgeocoder clplacemark


    【解决方案1】:

    SWIFT 4.2:编辑


    MapKit 框架确实提供了一种从坐标中获取地址详细信息的方法。

    您需要使用地图套件的反向地理编码CLGeocoder 类用于从地址获取位置,从位置(坐标)获取地址。 reverseGeocodeLocation 方法会从坐标返回地址详情。

    该方法接受CLLocation作为参数并返回CLPlacemark,其中包含地址字典。

    所以现在上面的方法将更新为:

    @objc func didLongPressMap(sender: UILongPressGestureRecognizer) {
    
        if sender.state == UIGestureRecognizer.State.began {
            let touchPoint = sender.location(in: mapView)
            let touchCoordinate = mapView.convert(touchPoint, toCoordinateFrom: self.mapView)
            let annotation = MKPointAnnotation()
            annotation.coordinate = touchCoordinate
            annotation.title = "Your position"
            mapView.addAnnotation(annotation) //drops the pin
            print("lat:  \(touchCoordinate.latitude)")
            let num = touchCoordinate.latitude as NSNumber
            let formatter = NumberFormatter()
            formatter.maximumFractionDigits = 4
            formatter.minimumFractionDigits = 4
            _ = formatter.string(from: num)
            print("long: \(touchCoordinate.longitude)")
            let num1 = touchCoordinate.longitude as NSNumber
            let formatter1 = NumberFormatter()
            formatter1.maximumFractionDigits = 4
            formatter1.minimumFractionDigits = 4
            _ = formatter1.string(from: num1)
            self.adressLoLa.text = "\(num),\(num1)"
    
            // Add below code to get address for touch coordinates.
            let geoCoder = CLGeocoder()
            let location = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)
            geoCoder.reverseGeocodeLocation(location, completionHandler:
                {
                    placemarks, error -> Void in
    
                    // Place details
                    guard let placeMark = placemarks?.first else { return }
    
                    // Location name
                    if let locationName = placeMark.location {
                        print(locationName)
                    }
                    // Street address
                    if let street = placeMark.thoroughfare {
                        print(street)
                    }
                    // City
                    if let city = placeMark.subAdministrativeArea {
                        print(city)
                    }
                    // Zip code
                    if let zip = placeMark.isoCountryCode {
                        print(zip)
                    }
                    // Country
                    if let country = placeMark.country {
                        print(country)
                    }
            })
        }
    }
    

    【讨论】:

    • 使用地址键的条件解包而不是两次提取键值不是更好吗?
    • 我不是这个意思。应该是if let locationName = placeMark.addressDictionary["Name"] { println(locationName) }
    • @Abizern 你的意思是用字符串初始化键? (使用条件运算符)对吗?
    • 为什么要转换成 NSString?您所做的只是打印结果。
    • 这是因为如果我们不投射它,那么它将打印Optional(city)。所以它只是为了删除这个烦人的可选关键字。
    【解决方案2】:

    对于 Swift 3:Swift 4

    首先您需要在info.plist 中设置接收用户 GPS 的限额。

    使用随机字符串设置:NSLocationWhenInUseUsageDescription。 和/或:NSLocationAlwaysUsageDescription 带有随机字符串。

    然后我设置了一个类来获取所需的数据,例如 zip、城镇、国家...:

    import Foundation
    import MapKit
    
    typealias JSONDictionary = [String:Any]
    
    class LocationServices {
    
        let shared = LocationServices()
        let locManager = CLLocationManager()
        var currentLocation: CLLocation!
    
        let authStatus = CLLocationManager.authorizationStatus()
        let inUse = CLAuthorizationStatus.authorizedWhenInUse
        let always = CLAuthorizationStatus.authorizedAlways
    
        func getAdress(completion: @escaping (_ address: JSONDictionary?, _ error: Error?) -> ()) {
    
            self.locManager.requestWhenInUseAuthorization()
    
            if self.authStatus == inUse || self.authStatus == always {
    
                self.currentLocation = locManager.location
    
                let geoCoder = CLGeocoder()
    
                geoCoder.reverseGeocodeLocation(self.currentLocation) { placemarks, error in
    
                    if let e = error {
    
                        completion(nil, e)
    
                    } else {
    
                        let placeArray = placemarks as? [CLPlacemark]
    
                        var placeMark: CLPlacemark!
    
                        placeMark = placeArray?[0]
    
                        guard let address = placeMark.addressDictionary as? JSONDictionary else {
                            return
                        }
    
                        completion(address, nil)
    
                    }
    
                }
    
            }
    
        }
    
    }
    

    调用者:

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            LocationServices.shared.getAdress { address, error in
    
                if let a = address, let city = a["City"] as? String {
                   //
                }
    
            }
    
        }
    
    }
    

    完成

    【讨论】:

    • 谢谢,一个很好的解决方案。我做了一些改动,currentLocation 是空的,所以我从 viewController 传递了它。
    【解决方案3】:
    import Foundation
    import CoreLocation
    import PlaygroundSupport
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    let location = CLLocation(latitude: 37.3321, longitude: -122.0318)
    CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
        
        guard let placemark = placemarks?.first else {
            let errorString = error?.localizedDescription ?? "Unexpected Error"
            print("Unable to reverse geocode the given location. Error: \(errorString)")
            return
        }
        
        let reversedGeoLocation = ReversedGeoLocation(with: placemark)
        print(reversedGeoLocation.formattedAddress)
        // Apple Inc.,
        // 1 Infinite Loop,
        // Cupertino, CA 95014
        // United States
    }
    
    struct ReversedGeoLocation {
        let name: String            // eg. Apple Inc.
        let streetNumber: String    // eg. 1
        let streetName: String      // eg. Infinite Loop
        let city: String            // eg. Cupertino
        let state: String           // eg. CA
        let zipCode: String         // eg. 95014
        let country: String         // eg. United States
        let isoCountryCode: String  // eg. US
        
        var formattedAddress: String {
            return """
            \(name),
            \(streetNumber) \(streetName),
            \(city), \(state) \(zipCode)
            \(country)
            """
        }
        
        // Handle optionals as needed
        init(with placemark: CLPlacemark) {
            self.name           = placemark.name ?? ""
            self.streetName     = placemark.thoroughfare ?? ""
            self.streetNumber   = placemark.subThoroughfare ?? ""
            self.city           = placemark.locality ?? ""
            self.state          = placemark.administrativeArea ?? ""
            self.zipCode        = placemark.postalCode ?? ""
            self.country        = placemark.country ?? ""
            self.isoCountryCode = placemark.isoCountryCode ?? ""
        }
    }
    

    旧的/已弃用的答案:

    感谢@Kampai's answer,这是一种兼容Swift 3更安全的方式(不强制!):

    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)
    
    geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
        guard let addressDict = placemarks?[0].addressDictionary else {
            return
        }
        
        // Print each key-value pair in a new row
        addressDict.forEach { print($0) }
        
        // Print fully formatted address
        if let formattedAddress = addressDict["FormattedAddressLines"] as? [String] {
            print(formattedAddress.joined(separator: ", "))
        }
        
        // Access each element manually
        if let locationName = addressDict["Name"] as? String {
            print(locationName)
        }
        if let street = addressDict["Thoroughfare"] as? String {
            print(street)
        }
        if let city = addressDict["City"] as? String {
            print(city)
        }
        if let zip = addressDict["ZIP"] as? String {
            print(zip)
        }
        if let country = addressDict["Country"] as? String {
            print(country)
        }
    })
    

    别忘了NSLocationWhenInUseUsageDescriptionNSLocationAlwaysUsageDescription

    【讨论】:

      【解决方案4】:

      感谢@Kampi。这是更新的 Swift 2.0 (Xcode 7) 版本:

      func setUsersClosestCity()
      {
          let geoCoder = CLGeocoder()
          let location = CLLocation(latitude: _point1.coordinate.latitude, longitude: _point1.coordinate.longitude)
          geoCoder.reverseGeocodeLocation(location)
          {
              (placemarks, error) -> Void in
      
              let placeArray = placemarks as [CLPlacemark]!
      
              // Place details
              var placeMark: CLPlacemark!
              placeMark = placeArray?[0]
      
              // Address dictionary
              print(placeMark.addressDictionary)
      
              // Location name
              if let locationName = placeMark.addressDictionary?["Name"] as? NSString
              {
                  print(locationName)
              }
      
              // Street address
              if let street = placeMark.addressDictionary?["Thoroughfare"] as? NSString
              {
                  print(street)
              }
      
              // City
              if let city = placeMark.addressDictionary?["City"] as? NSString
              {
                  print(city)
              }
      
              // Zip code
              if let zip = placeMark.addressDictionary?["ZIP"] as? NSString
              {
                  print(zip)
              }
      
              // Country
              if let country = placeMark.addressDictionary?["Country"] as? NSString
              {
                  print(country)
              }
          }
      }
      

      【讨论】:

      • 你有没有遇到过多次运行这个循环的问题?我的代码有问题,它会在停止之前输出我的城市名称三次,这会导致我的应用程序出现问题。 my SO post/question is here
      【解决方案5】:

      感谢@Kampai 的回答,我稍作修改,使其适用于Swift 1.2

              var geocoder = CLGeocoder()
              var location = CLLocation(latitude: IC.coordinate!.latitude, longitude: IC.coordinate!.longitude)
              geocoder.reverseGeocodeLocation(location) {
                  (placemarks, error) -> Void in
                  if let placemarks = placemarks as? [CLPlacemark] where placemarks.count > 0 {
                      var placemark = placemarks[0]
                      println(placemark.addressDictionary)
              }
      

      结果:

      [
          SubLocality: Sydney, 
          Street: 141 Harrington Street, 
          State: NSW, 
          SubThoroughfare: 141, 
          CountryCode: AU, ZIP: 2000, 
          Thoroughfare: Harrington Street, 
          Name: 141 Harrington Street, 
          Country: Australia, FormattedAddressLines: (
              "141 Harrington Street",
              "The Rocks NSW 2000",
              Australia
          ), 
          City: The Rocks
      ]
      

      【讨论】:

      • 感谢 superarts.org。
      【解决方案6】:

      斯威夫特 4.2 尽量保持简单,看苹果doc,根据需要修改:

      func retreiveCityName(lattitude: Double, longitude: Double, completionHandler: @escaping (String?) -> Void)
      {
          let geocoder = CLGeocoder()
          geocoder.reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler:
          {
              placeMarks, error in
      
              completionHandler(placeMarks?.first?.locality)
           })
      }
      

      【讨论】:

        【解决方案7】:

        更新Swift 4

        addressDictionary 在 iOS 11.0 中为 deprecated

        let geoCoder = CLGeocoder()
        let location = CLLocation(latitude: 37.769193, longitude: -122.426512)
        geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
        
        // Place details
        var placeMark: CLPlacemark!
        placeMark = placemarks?[0]
        
        // Complete address as PostalAddress
        print(placeMark.postalAddress as Any)  //  Import Contacts
        
        // Location name
        if let locationName = placeMark.name  {
            print(locationName)
        }
        
        // Street address
        if let street = placeMark.thoroughfare {
           print(street)
        }
        
        // Country
        if let country = placeMark.country {
           print(country)
        }
        })
        

        可以检索更多数据

        名称、通道、subThoroughfare、地点、subLocality、administrativeArea、subAdministrativeArea、postalcode、isoCountryCode、country、inlandWater、areaOfInterest

        【讨论】:

        • 为了通过这种方法获取城市名称,您必须使用 placeMark.locality,感谢它对我有用
        • 是的。在描述中查找更多信息。
        【解决方案8】:

        在 didUpdateToLocation 方法中:

        - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:
            (CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
            CLLocation *location = [locationManager location];
        
        
            CLLocationCoordinate2D coordinate = [location coordinate];
        
        
            latitude = [NSString stringWithFormat:@"%.12f", coordinate.latitude];
            longitude = [NSString stringWithFormat:@"%.12f", coordinate.longitude];
        
            CLLocation *location1 = [[CLLocation alloc]
                                     initWithLatitude:latitude.floatValue
                                     longitude:longitude.floatValue];
        
            self.myGeocoder = [[CLGeocoder alloc] init];
        
            [self.myGeocoder
             reverseGeocodeLocation:location1
             completionHandler:^(NSArray *placemarks, NSError *error) {
                if (error == nil &&
                     [placemarks count] > 0){
                    placemark = [placemarks lastObject];
                    NSString*    vendorLocation=[NSString stringWithFormat:@"%@ %@",
                                                  placemark.locality,
                                                  placemark.subLocality];
                    NSLog(@"%@",vendorLocation);
                }
            }];
        }
        

        【讨论】:

          【解决方案9】:
          func placePicker(_ viewController: GMSPlacePickerViewController, didPick place: GMSPlace) {
          
              viewController.dismiss(animated: true, completion: nil)
              let geoCoder = CLGeocoder()
              let location = CLLocation(latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
              geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
          
                  // Place details
                  var placeMark: CLPlacemark!
                  placeMark = placemarks?[0]
          
                  // Address dictionary
                  print(placeMark.addressDictionary as Any)
             // 
          
              print("Place name \(place.name)")
              print("Place address \(String(describing: place.formattedAddress))")
              print("Place attributions \(String(describing: place.attributions))")
          
          
          
          })
          }
          

          使用此代码将解决问题。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-11-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多