ios 在Swift中从经纬度查找城市名称和国家/地区

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/44031257/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 09:08:03  来源:igfitidea点击:

Find city name and country from latitude and longitude in Swift

iosswiftlocationcore-locationlatitude-longitude

提问by joshua

I'm working on application in Swift3 and I have letter problem i can't find the answer for it.

我正在 Swift3 中处理应用程序,但遇到了字母问题,我找不到答案。

How can I know city name and country short names base on latitude and longitude?

如何根据纬度和经度知道城市名称和国家/地区简称?

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate{
    let locationManager = CLLocationManager()
    var latitude: Double = 0
    var longitude: Double = 0
    override func viewDidLoad() {
        super.viewDidLoad()
        // For use when the app is open & in the background
        locationManager.requestAlwaysAuthorization()
        // For use when the app is open
        //locationManager.requestWhenInUseAuthorization()
        locationManager.delegate = self
        locationManager.startUpdatingLocation()
        if CLLocationManager.locationServicesEnabled() {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
        }
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.first {
            print(location.coordinate)
            latitude = location.coordinate.latitude
            longitude = location.coordinate.longitude
        }
    }
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if (status == CLAuthorizationStatus.denied){
            showLocationDisabledpopUp()
        }
    }
    func showLocationDisabledpopUp() {
        let alertController = UIAlertController(title: "Background Location Access  Disabled", message: "We need your location", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)
        let openAction = UIAlertAction(title: "Open Setting", style: .default) { (action) in
            if let url = URL(string: UIApplicationOpenSettingsURLString){
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            }
        }
        alertController.addAction(openAction)
        self.present(alertController, animated: true, completion: nil)
    }
}

采纳答案by pomo_mondreganto

I would recommend integrating Google Maps APIwith your project. If you do, your task can be achieved using Reverse GeocodingGoogle provides.

我建议将Google Maps API与您的项目集成。如果这样做,则可以使用Google 提供的反向地理编码来完成您的任务。

Furthermore, Google there is Google Maps SDKfor IOS development, which is also worth considering.

此外,谷歌还有用于IOS开发的谷歌地图SDK,也值得考虑。

UPD:You can do that without integrating maps into your project. Basing on thisanswer, you can achieve that using http requests to Google API. The request to:

UPD:您可以在不将地图集成到您的项目中的情况下做到这一点。基于答案,您可以使用对 Google API 的 http 请求来实现。请求:

https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&key=API_KEY 

would return JSONobject with information about the requested place, including country and city name.

将返回JSON带有所请求地点信息的对象,包括国家和城市名称。

BTW, I highly recommend using Alamofireto make http requests in Swift.

顺便说一句,我强烈建议使用Alamofire在 Swift 中发出 http 请求。

回答by Leo Dabus

You can use CLGeocoder reverseGeocodeLocationmethod to fetch a CLPlacemarkand get its countryand localityproperties info. Note that it is an asynchronous method so you will need to add a completion handler to your method when fetching that info:

您可以使用 CLGeocoder reverseGeocodeLocation方法来获取CLPlacemark并获取其国家地区属性信息。请注意,它是一个异步方法,因此您需要在获取该信息时向您的方法添加一个完成处理程序:

import UIKit
import MapKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

extension CLLocation {
    func fetchCityAndCountry(completion: @escaping (_ city: String?, _ country:  String?, _ error: Error?) -> ()) {
        CLGeocoder().reverseGeocodeLocation(self) { completion(
let location = CLLocation(latitude: -22.963451, longitude: -43.198242)
location.fetchCityAndCountry { city, country, error in
    guard let city = city, let country = country, error == nil else { return }
    print(city + ", " + country)  // Rio de Janeiro, Brazil
}
?.first?.locality,
let locationManager = CLLocationManager()
var location: CLLocation?

let geocoder = CLGeocoder()
var placemark: CLPlacemark?

// here I am declaring the iVars for city and country to access them later

var city: String?
var country: String?
var countryShortName: String?
?.first?.country, ) } } }


Usage

用法

func startLocationManager() {
    // always good habit to check if locationServicesEnabled
    if CLLocationManager.locationServicesEnabled() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()
    }
}

回答by Khalid Afridi

What you need is called reverse geocoding. As you have already declared some properties at the top. You need to add the CLGeocoder& CLPlancemark

您需要的是反向地理编码。因为您已经在顶部声明了一些属性。您需要添加CLGeocoderCLPlancemark

func stopLocationManager() {
   locationManager.stopUpdatingLocation()
   locationManager.delegate = nil
}

Create a function where you can start the location services

创建一个功能,您可以在其中启动位置服务

override func viewDidLoad() {
super.viewDidLoad()

    let authStatus = CLLocationManager.authorizationStatus()
    if authStatus == .notDetermined {
        locationManager.requestWhenInUseAuthorization()
    }

    if authStatus == .denied || authStatus == .restricted {
        // add any alert or inform the user to to enable location services 
    }

   // here you can call the start location function
   startLocationManager()

}

also create another to stop once you're done with location geocoding

完成位置地理编码后,还可以创建另一个停止

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    // print the error to see what went wrong
    print("didFailwithError\(error)")
    // stop location manager if failed
    stopLocationManager()
}

in view didLoad or from anywhere you want to start the location manager add a check first

在查看 didLoad 或从任何你想启动位置管理器的地方先添加一个检查

 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    // if you need to get latest data you can get locations.last to check it if the device has been moved
    let latestLocation = locations.last!

    // here check if no need to continue just return still in the same place
    if latestLocation.horizontalAccuracy < 0 {
        return
    }
    // if it location is nil or it has been moved
    if location == nil || location!.horizontalAccuracy > lastLocation.horizontalAccuracy {

        location = lastLocation
        // stop location manager
        stopLocationManager()

        // Here is the place you want to start reverseGeocoding
        geocoder.reverseGeocodeLocation(lastLocation, completionHandler: { (placemarks, error) in
                // always good to check if no error
                // also we have to unwrap the placemark because it's optional
                // I have done all in a single if but you check them separately 
                if error == nil, let placemark = placemarks, !placemark.isEmpty {
                    self.placemark = placemark.last
                }
                // a new function where you start to parse placemarks to get the information you need
                self.parsePlacemarks()

           })
    }
}

implement the delegate methods for location manager didFailedWithError

实现位置管理器的委托方法 didFailedWithError

parsePlacemarks() {
   // here we check if location manager is not nil using a _ wild card 
   if let _ = location {
        // unwrap the placemark 
        if let placemark = placemark {
            // wow now you can get the city name. remember that apple refers to city name as locality not city
            // again we have to unwrap the locality remember optionalllls also some times there is no text so we check that it should not be empty
            if let city = placemark.locality, !city.isEmpty {
                // here you have the city name
                // assign city name to our iVar
                self.city = city
            }
            // the same story optionalllls also they are not empty
            if let country = placemark.country, !country.isEmpty {

                self.country = country
            }
            // get the country short name which is called isoCountryCode
            if let countryShortName = placemark.isoCountryCode, !countryShortName.isEmpty {

                self.countryShortName = countryShortName
            }

        }


    } else {
       // add some more check's if for some reason location manager is nil
    }

}

implement the delegate method for location manager didUpdateLocations

实现位置管理器 didUpdateLocations 的委托方法

  var locationManager = CLLocationManager()

  override func viewDidLoad() {
    super.viewDidLoad()
    locationManager.delegate = self
    locationManager.requestWhenInUseAuthorization()
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.startUpdatingLocation()
    locationManager.startMonitoringSignificantLocationChanges()
    // Here you can check whether you have allowed the permission or not.
    if CLLocationManager.locationServicesEnabled()
    {
        switch(CLLocationManager.authorizationStatus())
        {
        case .authorizedAlways, .authorizedWhenInUse:
            print("Authorize.")
            let latitude: CLLocationDegrees = (locationManager.location?.coordinate.latitude)!
            let longitude: CLLocationDegrees = (locationManager.location?.coordinate.longitude)!
            let location = CLLocation(latitude: latitude, longitude: longitude) //changed!!!
            CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
                if error != nil {
                    return
                }else if let country = placemarks?.first?.country,
                    let city = placemarks?.first?.locality {
                    print(country)
                    self.cityNameStr = city
                }
                else {
                }
            })
            break

        case .notDetermined:
            print("Not determined.")
            self.showAlertMessage(messageTitle: "Bolo Board", withMessage: "Location service is disabled!!")
            break

        case .restricted:
            print("Restricted.")
            self.showAlertMessage(messageTitle: "Bolo Board", withMessage: "Location service is disabled!!")
            break

        case .denied:
            print("Denied.")
        }
    }
}

func showAlertMessage(messageTitle: NSString, withMessage: NSString) ->Void  {
    let alertController = UIAlertController(title: messageTitle as String, message: withMessage as String, preferredStyle: .alert)
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action:UIAlertAction!) in

    }
    alertController.addAction(cancelAction)

    let OKAction = UIAlertAction(title: "Settings", style: .default) { (action:UIAlertAction!) in
        if let url = URL(string: "App-Prefs:root=Privacy&path=LOCATION/com.company.AppName") {
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                // Fallback on earlier versions
            }
        }
    }
    alertController.addAction(OKAction)
    self.present(alertController, animated: true, completion:nil)
}

Add the parsePlacemarks function

添加 parsePlacemarks 函数

import Foundation
import CoreLocation

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 streetName: String      // eg. Infinite Loop
    let streetNumber: String    // eg. 1
    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 ?? ""
    }
}

You have to cmd+click on CLPlacemark to see all the properties that you can access for example street name is called thoroughfare& the number is is called subThoroughfarecontinue reading the documentation for more information

你必须在cmd +点击CLPlacemark看到所有的属性,你可以例如街道名称访问被称为通道和数量被称为subThoroughfare继续阅读文档的详细信息

Note: You have to check for locations error also geocoder error which I haven't implemented here but you have to take care of those errors and the best place to check error codes and everything else is apples documentation

注意:您必须检查位置错误和地理编码器错误,我没有在此处实施,但您必须处理这些错误以及检查错误代码和其他所有内容的最佳位置是苹果文档

Update: Check paresPlacemarks function where I added isoCountryCode which is equal to country shortName No need to add extra network calls to google API and Alamofire while your already using location services

更新:检查 paresPlacemarks 函数,其中我添加了 isoCountryCode,它等于 country shortName 在您已经使用位置服务时无需向 google API 和 Alamofire 添加额外的网络调用

回答by Mannam Brahmam

Here is the Swift 4 code:

这是 Swift 4 代码:

    import UIKit

    import CoreLocation 

    class MyViewController:UIViewController,CLLocationManagerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()


        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.requestAlwaysAuthorization()
        self.locationManager.startUpdatingLocation()


}

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {



        if( CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
            CLLocationManager.authorizationStatus() ==  .authorizedAlways){

           if let currentLocation = locationManager.location
           {

           if NetworkFunctions.NetworkRechability()
           {

            getAddressFromLatLon(pdblLatitude: "\(Double((currentLocation.coordinate.latitude)))", withLongitude: "\(Double((currentLocation.coordinate.longitude)))")

            }

            }
        }



    }

    func getAddressFromLatLon(pdblLatitude: String, withLongitude pdblLongitude: String) {
        var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
        let lat: Double = Double("\(pdblLatitude)")!

        let lon: Double = Double("\(pdblLongitude)")!

        let ceo: CLGeocoder = CLGeocoder()
        center.latitude = lat
        center.longitude = lon

        let loc: CLLocation = CLLocation(latitude:center.latitude, longitude: center.longitude)


        ceo.reverseGeocodeLocation(loc, completionHandler:
            {(placemarks, error) in
                if (error != nil)
                {
                }

                if placemarks != nil
                {

                    let pm = placemarks! as [CLPlacemark]

                    if pm.count > 0 {

                        let pm = placemarks![0]

                        print(pm.country ?? "")
                        print(pm.locality ?? "")
                       print(pm.subLocality ?? "")
                       print(pm.thoroughfare ?? "")
                        print(pm.postalCode ?? "")
                        print(pm.subThoroughfare ?? "")
                        var addressString : String = ""
                        if pm.subLocality != nil {
                            addressString = addressString + pm.subLocality! + ", "
                        }
                        if pm.thoroughfare != nil {
                            addressString = addressString + pm.thoroughfare! + ", "
                        }
                        if pm.locality != nil {
                            addressString = addressString + pm.locality! + ", "
                            if pm.country != nil {
                                addressString = addressString + pm.country! + ", "
                                //uuuuu
                                if(location_city != pm.locality!.trimmingCharacters(in: .whitespaces))
                                {
                                    location_city=pm.locality!.trimmingCharacters(in: .whitespaces)
                                      DispatchQueue.main.async{
                                    self.GetBeeWatherDetails(district: pm.locality!, country: pm.country!)
                                    }
                                }
                            }

                        }

                        if pm.postalCode != nil {
                            addressString = addressString + pm.postalCode! + " "
                        }

                    }
                }
        })

    }

}

回答by Paulo Mattos

You can use CLGeocoder, from CoreLocation, for that. From Apple documentation (emphasizes mine):

CLGeocoder为此,您可以使用CoreLocation 中的 。来自 Apple 文档(强调我的):

A single-shot object for converting between geographic coordinates and place names.

The CLGeocoderclass provides services for converting between a coordinate (specified as a latitude and longitude) and the user-friendly representation of that coordinate. A user-friendly representation of the coordinate typically consists of the street, city, state, and countryinformation corresponding to the given location...

用于在地理坐标和地名之间转换的单次对象。

所述CLGeocoder类的坐标(指定为纬度和经度)和坐标的用户友好的表示之间进行转换提供服务。坐标的用户友好表示通常包括与给定位置相对应的街道、城市、州和国家信息......

This service is unrelated to MapKitand, as such, don't require you use/show a map in your app at all.

此服务与MapKit无关,因此根本不需要您在应用程序中使用/显示地图。

回答by Dilip Mishra

extension CLLocation {
func fetchAddress(completion: @escaping (_ address: String?, _ error: Error?) -> ()) {
    CLGeocoder().reverseGeocodeLocation(self) {
        let palcemark = 
 (myLocation as? CLLocation)!.fetchAddress { (address, error) in
                        guard let address = address, error == nil else                              
{return }
?.first var address = "" if let subThoroughfare = palcemark?.subThoroughfare { address = address + subThoroughfare + "," } if let thoroughfare = palcemark?.thoroughfare { address = address + thoroughfare + "," } if let locality = palcemark?.locality { address = address + locality + "," } if let subLocality = palcemark?.subLocality { address = address + subLocality + "," } if let administrativeArea = palcemark?.administrativeArea { address = address + administrativeArea + "," } if let postalCode = palcemark?.postalCode { address = address + postalCode + "," } if let country = palcemark?.country { address = address + country + "," } if address.last == "," { address = String(address.dropLast()) } completion(address,) // completion("\(
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))")



})
}
?.first?.subThoroughfare ?? ""), \(
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    let location: CLLocation = locations.last!
    print("Location: \(location)")

    let geocoder = CLGeocoder()
    geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
        // Process Response
        if let error = error {
            print("Unable to Reverse Geocode Location (\(error))")
        } else {
            if let placemarks = placemarks, let placemark = placemarks.first {
                self.city = placemark.locality!

                //self.country = placemark.country!
            }
        }
    }

    let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude,
                                          longitude: location.coordinate.longitude,
                                          zoom: zoomLevel)

     self.locationv = CLLocation(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)

    if myView.isHidden {
        myView.isHidden = false
        myView.camera = camera
    } else {
        myView.animate(to: camera)
    }
}
?.first?.thoroughfare ?? ""), \(##代码##?.first?.locality ?? ""), \(##代码##?.first?.subLocality ?? ""), \(##代码##?.first?.administrativeArea ?? ""), \(##代码##?.first?.postalCode ?? ""), \(##代码##?.first?.country ?? "")",) } }

回答by Fansad PP

1 . import CoreLocation 2 . insert CLLocationManagerDelegate in your class 3 . Do the delegate methods described below... hope it will help you you can find city name and country through following these steps...Here is my code

1 . 导入 CoreLocation 2 。在你的类中插入 CLLocationManagerDelegate 3 。执行下面描述的委托方法...希望它可以帮助您通过以下步骤找到城市名称和国家...这是我的代码

##代码##

回答by bharathi kumar

Add this extension in your swift file.

将此扩展名添加到您的 swift 文件中。

##代码##

}

}

And then call it on any of the CLLocation object.

然后在任何 CLLocation 对象上调用它。

Eg:

例如:

##代码##

回答by Ayush Dixit

I had also the same issue .You can use this code.

我也有同样的问题。您可以使用此代码。

##代码##

Hope this will resolve your problem.

希望这能解决您的问题。

回答by Akbar Khan

This method will give you the current location, city name ,country name etc.

此方法将为您提供当前位置、城市名称、国家名称等。

##代码##