将坐标转换为城市名称?

Posted

技术标签:

【中文标题】将坐标转换为城市名称?【英文标题】: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 中打印完整地址(街道、城市、邮编、国家/地区)。

【问题讨论】:

【参考方案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

addressDictionaryios 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))")



)

使用此代码将解决问题。

【讨论】:

以上是关于将坐标转换为城市名称?的主要内容,如果未能解决你的问题,请参考以下文章

使用时区城市坐标和当地时间进行时区转换[重复]

powerbi地图不能识别城市

arcgis打开的城市开发边界怎么跟2000坐标对不上

将窗口坐标转换为屏幕坐标??

怎么把坐标信息的Excel文件转换为KML文件

arcgis 2000经纬度坐标转换为2000平面坐标