web-dev-qa-db-ja.com

Swift-逆ジオコーディングから住所形式を生成

Swift 3.でCLGeocoderを使用してフォーマットされた完全なアドレスを生成しようとしています。これを参照しました [〜#〜] so [〜#〜] スレッド以下のコードを取得します。

ただし、次の行で 'nil'エラーが発生してアプリがクラッシュする場合があります。

//Address dictionary
print(placeMark.addressDictionary ?? "")

質問:

  1. GeoCoderから取得したこれらの値を連結して完全な住所を形成するにはどうすればよいですか? (通り+都市+など)
  2. Funcがアドレスを見つけられない場合に発生するnilエラーを処理するにはどうすればよいですか?

完全なコード:

func getAddress() -> String {
        var address: String = ""

        let geoCoder = CLGeocoder()
        let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
        //selectedLat and selectedLon are double values set by the app in a previous process

        geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in

            // Place details
            var placeMark: CLPlacemark!
            placeMark = placemarks?[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)
            }

        })

        return address;
    } 
18
Dinuka
func getAddressFromLatLon(pdblLatitude: String, withLongitude pdblLongitude: String) {
        var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
        let lat: Double = Double("\(pdblLatitude)")!
        //21.228124
        let lon: Double = Double("\(pdblLongitude)")!
        //72.833770
        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)
                {
                    print("reverse geodcode fail: \(error!.localizedDescription)")
                }
                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! + ", "
                    }
                    if pm.postalCode != nil {
                        addressString = addressString + pm.postalCode! + " "
                    }


                    print(addressString)
              }
        })

    }
53

各国には独自の形式があるため、住所のフォーマットは困難です。

数行のコードで、各国の正しい住所形式を取得し、Appleの違いを処理できます。

IOS 11以降、連絡先フレームワークアドレスを取得できます。

extension CLPlacemark {
    @available(iOS 11.0, *)
    open var postalAddress: CNPostalAddress? { get }
}

この拡張機能は、Contactsフレームワークの一部です。つまり、この機能はXCodeコード補完では表示されません。

import Contacts

この追加のインポートにより、次のようなことができます

CLGeocoder().reverseGeocodeLocation(location, preferredLocale: nil) { (clPlacemark: [CLPlacemark]?, error: Error?) in
    guard let place = clPlacemark?.first else {
        print("No placemark from Apple: \(String(describing: error))")
        return
    }

    let postalAddressFormatter = CNPostalAddressFormatter()
    postalAddressFormatter.style = .mailingAddress
    var addressString: String?
    if let postalAddress = place.postalAddress {
        addressString = postalAddressFormatter.string(from: postalAddress)
    }
}

住所の国の形式でフォーマットされた住所を取得します。

フォーマッタは、attributedStringとしてのフォーマットもサポートしています。

IOS 11より前は、CLPlacemarkCNPostalAddressに変換できますが、国固有のCNPostalAddressFormatterのフォーマットを使用できます。

11
Gerd Castan

これはSwift 3の私のコードです

func getAdressName(coords: CLLocation) {

    CLGeocoder().reverseGeocodeLocation(coords) { (placemark, error) in
            if error != nil {
                print("Hay un error")
            } else {

                let place = placemark! as [CLPlacemark]
                if place.count > 0 {
                    let place = placemark![0]
                    var adressString : String = ""
                    if place.thoroughfare != nil {
                        adressString = adressString + place.thoroughfare! + ", "
                    }
                    if place.subThoroughfare != nil {
                        adressString = adressString + place.subThoroughfare! + "\n"
                    }
                    if place.locality != nil {
                        adressString = adressString + place.locality! + " - "
                    }
                    if place.postalCode != nil {
                        adressString = adressString + place.postalCode! + "\n"
                    }
                    if place.subAdministrativeArea != nil {
                        adressString = adressString + place.subAdministrativeArea! + " - "
                    }
                    if place.country != nil {
                        adressString = adressString + place.country!
                    }

                    self.lblPlace.text = adressString
                }
            }
        }
  }

次のような関数を簡単にセル化できます。

let cityCoords = CLLocation(latitude: newLat, longitude: newLon)
cityData(coord: cityCoords)
6
oscar castellon

連結するには、単にreturn address これで :

return "\(locationName), \(street), \(city), \(Zip), \(country)"
2
Makaille

シンプルにする-完全なSwift 3および4互換のView Controllerの例で、ユーザーの場所からフォーマットされたアドレス文字列を取得する( CLPlacemark で利用可能な他のキーを追加する文字列に詳細情報が必要です):

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

let manager = CLLocationManager()
let geocoder = CLGeocoder()

var locality = ""
var administrativeArea = ""
var country = ""

override func viewDidLoad() {
    super.viewDidLoad()

    manager.delegate = self
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.requestWhenInUseAuthorization()
    manager.startUpdatingLocation()

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let location = locations[0]
        manager.stopUpdatingLocation()

    geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) in
        if (error != nil) {
            print("Error in reverseGeocode")
            }

        let placemark = placemarks! as [CLPlacemark]
        if placemark.count > 0 {
            let placemark = placemarks![0]
            self.locality = placemark.locality!
            self.administrativeArea = placemark.administrativeArea!
            self.country = placemark.country!
        }
    })
}

func userLocationString() -> String {
    let userLocationString = "\(locality), \(administrativeArea), \(country)"
    return userLocationString
}

}

この例でprint(userLocationString())を呼び出すと、郊外、州、国が印刷されます。

忘れずにプライバシー-使用中の場所使用方法の説明を事前にInfo.plistファイルに追加して、位置情報サービスを利用するための許可をアプリに付与するユーザー。

2
elarcoiris
  1. 空のアドレスの問題を修正するには、クラスプロパティを使用して追加された値を保持するか、クロージャーを使用して値を呼び出し元の関数に返すことができます。
  2. クラッシュを修正するには、オプションの強制的なアンラップを回避する必要があります

クロージャを使用すると、次のようにできます。

// Using closure
func getAddress(handler: (String) -> Void)
{
    var address: String = ""
    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
    //selectedLat and selectedLon are double values set by the app in a previous process

    geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in

        // Place details
        var placeMark: CLPlacemark?
        placeMark = placemarks?[0]

        // Address dictionary
        //print(placeMark.addressDictionary ?? "")

        // Location name
        if let locationName = placeMark?.addressDictionary?["Name"] as? String {
            address += locationName + ", "
        }

        // Street address
        if let street = placeMark?.addressDictionary?["Thoroughfare"] as? String {
            address += street + ", "
        }

        // City
        if let city = placeMark?.addressDictionary?["City"] as? String {
            address += city + ", "
        }

        // Zip code
        if let Zip = placeMark?.addressDictionary?["Zip"] as? String {
            address += Zip + ", "
        }

        // Country
        if let country = placeMark?.addressDictionary?["Country"] as? String {
            address += country
        }

       // Passing address back
       handler(address)
    })
}

次のようなメソッドを呼び出すことができます。

getAddress { (address) in
    print(address)
}
1
Midhun MP
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: vehicleLocation.latitude, longitude: vehicleLocation.latitude), completionHandler: {(placemarks, error) -> Void in

  guard error == nil else {completionHandler(nil); return}

  guard let place = placemarks else {completionHandler(nil); return}

  if place.count > 0 {
    let pm = place[0]

    var addArray:[String] = []
    if let name = pm.name {
      addArray.append(name)
    }
    if let thoroughfare = pm.thoroughfare {
      addArray.append(thoroughfare)
    }
    if let subLocality = pm.subLocality {
      addArray.append(subLocality)
    }
    if let locality = pm.locality {
      addArray.append(locality)
    }
    if let subAdministrativeArea = pm.subAdministrativeArea {
      addArray.append(subAdministrativeArea)
    }
    if let administrativeArea = pm.administrativeArea {
      addArray.append(administrativeArea)
    }
    if let country = pm.country {
      addArray.append(country)
    }
    if let postalCode = pm.postalCode {
      addArray.append(postalCode)
    }

    let addressString = addArray.joined(separator: ",\n")

    print(addressString)

    completionHandler(addressString)
  }
  else { completionHandler(nil)}
})
0

ジオコーディング用に独自の静的クラスを作成し、CLPlacemarkの属性を取得し、「通常」Googleを返すような完全なアドレスを取得します。

import Foundation
import CoreLocation

class ReverseGeocoding {

    static func geocode(latitude: Double, longitude: Double, completion: @escaping (CLPlacemark?, _ completeAddress: String?, Error?) -> ())  {
        CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude)) { placemarks, error in
            guard let placemark = placemarks?.first, error == nil else {
                completion(nil, nil, error)
                return
            }

            let completeAddress = getCompleteAddress(placemarks)

            completion(placemark, completeAddress, nil)
        }
    }

    static private func getCompleteAddress(_ placemarks: [CLPlacemark]?) -> String {
        guard let placemarks = placemarks else {
            return ""
        }

        let place = placemarks as [CLPlacemark]
        if place.count > 0 {
            let place = placemarks[0]
            var addressString : String = ""
            if place.thoroughfare != nil {
                addressString = addressString + place.thoroughfare! + ", "
            }
            if place.subThoroughfare != nil {
                addressString = addressString + place.subThoroughfare! + ", "
            }
            if place.locality != nil {
                addressString = addressString + place.locality! + ", "
            }
            if place.postalCode != nil {
                addressString = addressString + place.postalCode! + ", "
            }
            if place.subAdministrativeArea != nil {
                addressString = addressString + place.subAdministrativeArea! + ", "
            }
            if place.country != nil {
                addressString = addressString + place.country!
            } 

            return addressString
        }
        return ""
    }
}

次に、実装:

    ReverseGeocoding.geocode(coordinate: coordinate, completion: { (placeMark, completeAddress, error) in

        if let placeMark = placeMark, let completeAddress = completeAddress {
            print(placeMark.postalCode)
            print(placeMark)
            print(completeAddress)
        } else {
            // do something with the error
        }

最後に印刷物:

15172
Calle del Arenal, 4, Calle del Arenal, 4, 15172 Oleiros, A Coruña, España @ <+43.33190337,-8.37144380> +/- 100.00m, region CLCircularRegion (identifier:'<+43.33190337,-8.37144380> radius 70.84', center:<+43.33190337,-8.37144380>, radius:70.84m)
Calle del Arenal, 4, Oleiros, 15172, A Coruña, España
0