【问题标题】:Reverse geocoding in Swift 4Swift 4 中的反向地理编码
【发布时间】:2018-04-02 19:45:17
【问题描述】:

我正在尝试编写一个简单的方法,该方法输入 CLLocationDegrees 并返回 CLPlacemark。看着Apple's documentation,这似乎是一个简单的任务。

下面是我扔到操场上的东西:

import CoreLocation
// this is necessary for async code in a playground
import PlaygroundSupport 
// this is necessary for async code in a playground
PlaygroundPage.current.needsIndefiniteExecution = true

func geocode(latitude: CLLocationDegrees, longitude: CLLocationDegrees) -> CLPlacemark? {
    let location = CLLocation(latitude: latitude, longitude: longitude)
    let geocoder = CLGeocoder()
    var placemark: CLPlacemark?
    geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
        if error != nil {
            print("something went horribly wrong")
        }
        if let placemarks = placemarks {
            placemark = placemarks.first
        }
    }
    return placemark
}
let myPlacemark = geocode(latitude: 37.3318, longitude: 122.0312)

就目前而言,我的方法返回 nil。我不确定我的错误在哪里,但我确信这对我来说是非常愚蠢的事情。感谢您的阅读。

【问题讨论】:

  • geocoder.reverseGeocodeLocation 是异步的,您需要一个完成处理程序
  • 谢谢。我看看能不能解决。
  • 我正在尝试传递在核心数据中存储为双精度的坐标。

标签: ios swift cllocationmanager


【解决方案1】:
import UIKit
import CoreLocation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

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

或者简单地说:

func geocode(latitude: Double, longitude: Double, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void)  {
    CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler: completion)
}

或扩展 CLLocation:

extension CLLocation {
    func geocode(completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
        CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
    }
}

要将您的地标格式化为邮寄地址,您可以使用联系人框架CNPostalAddressFormatter

import Contacts

extension Formatter {
    static let mailingAddress: CNPostalAddressFormatter = {
        let formatter = CNPostalAddressFormatter()
        formatter.style = .mailingAddress
        return formatter
    }()
}

extension CLPlacemark {
    var mailingAddress: String? {
        postalAddress?.mailingAddress
    }
}

extension CNPostalAddress {
    var mailingAddress: String {
        Formatter.mailingAddress.string(from: self)
    }
}

地标

包含一个 CLPlacemark 对象数组。对于大多数地理编码请求, 这个数组应该只包含一个条目。然而,前向地理编码 在以下情况下,请求可能会返回多个地标对象 指定的地址无法解析到单个位置。如果 请求被取消或获取地标时出错 信息,此参数为nil。

有关 CLPlacemark 属性的更多信息,您可以查看此CLPlacemark


用法:

let location = CLLocation(latitude: -22.963451, longitude: -43.198242)
location.geocode { placemark, error in
    if let error = error as? CLError {
        print("CLError:", error)
        return
    } else if let placemark = placemark?.first {
        // you should always update your UI in the main thread
        DispatchQueue.main.async {
            //  update UI here
            print("name:", placemark.name ?? "unknown")
            
            print("address1:", placemark.thoroughfare ?? "unknown")
            print("address2:", placemark.subThoroughfare ?? "unknown")
            print("neighborhood:", placemark.subLocality ?? "unknown")
            print("city:", placemark.locality ?? "unknown")
            
            print("state:", placemark.administrativeArea ?? "unknown")
            print("subAdministrativeArea:", placemark.subAdministrativeArea ?? "unknown")
            print("zip code:", placemark.postalCode ?? "unknown")
            print("country:", placemark.country ?? "unknown", terminator: "\n\n")
            
            print("isoCountryCode:", placemark.isoCountryCode ?? "unknown")
            print("region identifier:", placemark.region?.identifier ?? "unknown")
    
            print("timezone:", placemark.timeZone ?? "unknown", terminator:"\n\n")

            // Mailind Address
            print(placemark.mailingAddress ?? "unknown")
        }
    }
}

这将打印出来

名称:Morro da Saudade
地址1:木麻黄街
地址2:597
邻里:拉戈阿
城市:里约热内卢
状态:RJ
subAdministrativeArea:未知
邮编:22011-040
国家:巴西

isoCountryCode: BR
区域标识符: 半径 141.83
时区:America/Sao_Paulo(当前)

木麻黄街,597
拉戈阿
里约热内卢 RJ
22011-040
巴西

【讨论】:

  • 谢谢!这可以完成工作。我认为我不能像这样声明let 常量,因为它是异步代码。我会用别的东西重构这个let myPlacemark = geocode(latitude: 37.3318, longitude: 122.0312)
  • 你需要在闭包内使用它
  • 完美。谢谢!
  • 你好,如何获得完整的州名?,不是ISO代码格式。
  • @LeoDabus,我们怎样才能得到完整的州名,可以吗?
【解决方案2】:

在 Stack Overflow 上有很多关于“reverseGeocodeLocation”的问题,我见过很多。我正在使用 Swift 4.2,我认为我的“reverseGeocodeLocation”请求可能会超时。好吧,情况并非如此,当开始使用 iPhone 移动时,“didUpdateLocations”周期需要成熟。 'reverseGeocodeLocation' 返回地标之前可能需要两个或更多个周期。

我编写了一个简单的应用程序,它使用“reverseGeocodeLocation”,直到在重复“didUpdateLocations”循环后返回“placemarks”。这是我自己种植的“SampleCode”。我从使用该应用程序中学到了很多东西,它可能有助于其他人了解反向地理编码请求在现实世界中的工作原理。

请向您的 cmets 提供可能的改进。此代码是免费提供和免费获取的。

import Foundation
import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {
    @IBAction func RefreshLocationButton(_ sender: Any) {
        self.requestingPlacemark = true
        self.placemarkData = nil
        //^Make another Placemark Request
        self.requestCounter = 0
        self.RequestCounterLabel.text = ""
        self.LocationLabel.text = ""
        self.PlacemarkLabel.text = ""}
    @IBOutlet weak var LocationCounterLabel: UILabel!
    @IBOutlet weak var RequestCounterLabel: UILabel!
    @IBOutlet weak var LocationLabel: UILabel!
    @IBOutlet weak var PlacemarkLabel: UILabel!
    let locationManager = CLLocationManager()
    var placemarkData: CLPlacemark!
    var placemarkString: String!
    var printPlacemarkData: Bool!
    var didUpdateLocationsCounter: Int = 0
    var requestCounter: Int = 0
    var requestingPlacemark: Bool = true

override func viewDidLoad() {
    super.viewDidLoad()
    if CLLocationManager.locationServicesEnabled() {
        locationManager.requestAlwaysAuthorization()
        locationManager.delegate = self
        //locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        locationManager.startUpdatingLocation()
        self.RequestCounterLabel.text = ""
        self.LocationLabel.text = ""
        self.PlacemarkLabel.text = ""}}
func printPlacemarks() {
    if printPlacemarkData {
        self.placemarkString = "Placemark Data:"
        self.placemarkString = buildPlacemarkString(item: 1)
        self.placemarkString = buildPlacemarkString(item: 2)
        self.placemarkString = buildPlacemarkString(item: 3)
        self.placemarkString = buildPlacemarkString(item: 4)
        self.placemarkString = buildPlacemarkString(item: 5)
        self.placemarkString = buildPlacemarkString(item: 6)
        self.placemarkString = buildPlacemarkString(item: 7)
        self.placemarkString = buildPlacemarkString(item: 8)
        self.placemarkString = buildPlacemarkString(item: 9)
        self.placemarkString = buildPlacemarkString(item: 10)
        self.PlacemarkLabel.text = self.placemarkString
        self.printPlacemarkData = false}}
func buildPlacemarkString(item: Int) -> String {
    var elementText: String!
    var newString: String!
        switch item {
        case 1: elementText = "name: " + self.placemarkData.name!
        case 2: elementText = "subThoroughfare: " + self.placemarkData.subThoroughfare!
        case 3: elementText = "thoroughfare: " + self.placemarkData.thoroughfare!
        case 4: elementText = "postalCode: " + self.placemarkData.postalCode!
        case 5: elementText = "subLocality: " + self.placemarkData.subLocality!
        case 6: elementText = "locality: " + self.placemarkData.locality!
        case 7: elementText = "subAdministrativeArea: " + self.placemarkData.subAdministrativeArea!
        case 8: elementText = "administrativeArea: " + self.placemarkData.administrativeArea!
        case 9: elementText = "country: " + self.placemarkData.country!
        case 10: elementText = "isoCountryCode: " + self.placemarkData.isoCountryCode!
        default: print("Error: incorrect item number!")}
        newString = self.placemarkString + "\n" + elementText
    return newString
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        //location prints one time and 'didUpdateLocations' is stopped:
    self.didUpdateLocationsCounter = self.didUpdateLocationsCounter + 1
    let labelString = String(describing: self.didUpdateLocationsCounter)
    self.LocationCounterLabel.text = "Location Update Counter: " + labelString
    if self.placemarkData == nil {
        //location variable:
        self.requestCounter = self.requestCounter + 1
        let textString = String(describing: self.requestCounter)
        self.RequestCounterLabel.text = "Request Counter: " + textString
        if locations.count == 0 {
            self.LocationLabel.text = "Locations: There was NONE"}
        else {
            if locations.count > 0 {
                let coordinate2D: CLLocation = locations.first!
                let location = CLLocation(latitude: coordinate2D.coordinate.latitude, longitude: coordinate2D.coordinate.longitude)
                let printString = String(describing: location)
                self.LocationLabel.text = "Location: " + printString
                let geocoder: CLGeocoder = CLGeocoder()
                geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
                    if error != nil {
                        let errorString = String(describing: error?.localizedDescription)
                        print("reverse geodcode fail: \(errorString)")
                        self.LocationCounterLabel.text = ""
                        self.RequestCounterLabel.text = ""
                        self.LocationLabel.text = "Reverse Geodcode fail: \(errorString)"
                        self.PlacemarkLabel.text = ""
                        self.requestingPlacemark = false
                        return}
                    else {
                        let pm = placemarks! as [CLPlacemark]
                        //There is ALWAYS 'placemarks' Data
                        if pm.count > 0 {
                            self.placemarkData = placemarks![0]
                            self.printPlacemarkData = true
                            self.printPlacemarks()
                            self.requestingPlacemark = false}}})}
            else {
                if self.requestingPlacemark {
                    self.LocationLabel.text = "Problem: There is no 'location.first'"}}}}}}

然后是 Storyboard UI:

Storyboard Image

View Controller Image

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="MyThoroughfare" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location Counter" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZIg-u9-TMR">
                                <rect key="frame" x="16" y="20" width="343" height="20.5"/>
                                <color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location Count Label" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="csF-KS-xGE">
                                <rect key="frame" x="16" y="48" width="343" height="20.5"/>
                                <color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location String" lineBreakMode="tailTruncation" numberOfLines="8" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qOc-gk-gSp">
                                <rect key="frame" x="16" y="76" width="343" height="20.5"/>
                                <color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Placemark String" lineBreakMode="tailTruncation" numberOfLines="14" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="42A-nO-tNf">
                                <rect key="frame" x="16" y="104" width="343" height="20.5"/>
                                <color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HgN-oB-ds0">
                                <rect key="frame" x="121.5" y="578" width="132" height="30"/>
                                <state key="normal" title="Request Placemark"/>
                                <connections>
                                    <action selector="RefreshLocationButton:" destination="BYZ-38-t0r" eventType="touchUpInside" id="izY-S5-wjg"/>
                                </connections>
                            </button>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="ZIg-u9-TMR" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="6HO-kN-l1z"/>
                            <constraint firstItem="qOc-gk-gSp" firstAttribute="top" secondItem="csF-KS-xGE" secondAttribute="bottom" constant="7.5" id="6ux-dx-gJr"/>
                            <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="42A-nO-tNf" secondAttribute="trailing" constant="16" id="8dG-gV-Cob"/>
                            <constraint firstItem="HgN-oB-ds0" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="A38-Nt-i3D"/>
                            <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="ZIg-u9-TMR" secondAttribute="trailing" constant="16" id="Bb0-YC-oov"/>
                            <constraint firstItem="csF-KS-xGE" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="FJU-ha-HkL"/>
                            <constraint firstItem="42A-nO-tNf" firstAttribute="top" secondItem="qOc-gk-gSp" secondAttribute="bottom" constant="7.5" id="SpR-XB-MLG"/>
                            <constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="HgN-oB-ds0" secondAttribute="bottom" constant="59" id="Vr6-QE-230"/>
                            <constraint firstItem="42A-nO-tNf" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="aHo-kJ-aCb"/>
                            <constraint firstItem="csF-KS-xGE" firstAttribute="top" secondItem="ZIg-u9-TMR" secondAttribute="bottom" constant="7.5" id="c63-Mq-ItW"/>
                            <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="csF-KS-xGE" secondAttribute="trailing" constant="16" id="dlV-Hc-8XJ"/>
                            <constraint firstItem="qOc-gk-gSp" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="m5y-Q8-jxI"/>
                            <constraint firstItem="ZIg-u9-TMR" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="sdw-WQ-Bx9"/>
                            <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="qOc-gk-gSp" secondAttribute="trailing" constant="16" id="woB-ig-i5v"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                    </view>
                    <connections>
                        <outlet property="LocationCounterLabel" destination="ZIg-u9-TMR" id="K4g-WT-f82"/>
                        <outlet property="LocationLabel" destination="qOc-gk-gSp" id="Cdw-bw-EWt"/>
                        <outlet property="PlacemarkLabel" destination="42A-nO-tNf" id="dMh-bw-cRE"/>
                        <outlet property="RequestCounterLabel" destination="csF-KS-xGE" id="ai6-zn-Toi"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53.600000000000001" y="66.11694152923539"/>
        </scene>
    </scenes>
</document>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-14
    • 1970-01-01
    • 2015-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多