web-dev-qa-db-ja.com

Swift 4の逆ジオコーディング

私はCLLocationDegreesを与えられ、CLPlacemarkを返す簡単なメソッドを書き込もうとしています。 Appleのドキュメント を見ると、簡単なタスクのように思えます。

以下は私が遊び場に捨てたものです。

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を返しています。私のエラーがどこにあるのかはわかりませんが、それは私の側の主に愚かなものであると安心します。読んでくれてありがとう。

12
Adrian
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? {
        return postalAddress?.mailingAddress
    }
}

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

placemark

CLPlacemarkオブジェクトの配列が含まれます。ほとんどのジオコーディングリクエストでは、この配列にはエントリが1つだけ含まれている必要があります。ただし、指定された住所を単一の場所に解決できなかった場合、前方ジオコーディングリクエストは複数の目印オブジェクトを返すことがあります。リクエストがキャンセルされたか、目印情報の取得中にエラーが発生した場合、このパラメーターは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")
        }
    }
}

これは印刷されます

name: Morro da Saudade
address1: Rua Casuarina
address2: 597
neighborhood: Lagoa
city: Rio de Janeiro
state: RJ
subAdministrativeArea: unknown
Zip code: 22011-040
country: Brazil

isoCountryCode: BR
region identifier: <-22.96345100,-43.19824200> radius 141.83
timezone: America/Sao_Paulo (current)

Rua Casuarina、597

ラゴア

リオデジャネイロRJ

22011-040

ブラジル

24
Leo Dabus

Stack Overflowの 'reverseGeocodeLocation'に対処する質問は非常に多くあります。私はSwift 4.2を使用していますが、 'reverseGeocodeLocation'リクエストがタイムアウトするかもしれないと思っていました。これはそうではありません。 「reverseGeocodeLocation」が目印を返すまでに2サイクル以上かかる場合があります。

「didUpdateLocations」サイクルを繰り返して「placemarks」が返されるまで、「reverseGeocodeLocation」を使用するシンプルなアプリを作成しました。これは、私自身が育てた「SampleCode」です。このアプリの使用から多くのことを学びました。実際の世界で逆ジオコードリクエストがどのように機能するかを他の人が理解するのに役立つかもしれません。

改善の可能性についてコメントをお願いします。このコードは自由に与えられ、自由に使用されます。

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'"}}}}}}

そして、ストーリーボードUI:

ストーリーボード画像

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>
0
B. Wason