J'essaie d'écrire une méthode simple qui est nourrie avec CLLocationDegrees
et retourne une CLPlacemark
. En regardant la documentation d'Apple , cela semble être une tâche simple.
Voici ce que j'ai jeté dans un terrain de jeu:
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)
Dans l'état actuel des choses, ma méthode renvoie zéro. Je ne suis pas sûr de savoir où se situe mon erreur, mais je suis convaincu que c'est quelque chose d'étrange stupide de ma part. Merci pour la lecture.
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)
}
}
ou simplement:
func geocode(latitude: Double, longitude: Double, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler: completion)
}
ou extension de CLLocation:
extension CLLocation {
func geocode(completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
}
}
Pour formater votre marque de repère comme adresse postale, vous pouvez utiliser la structure de contacts 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)
}
}
repère
Contient un tableau d'objets CLPlacemark. Pour la plupart des demandes de géocodage, Ce tableau ne doit contenir qu'une seule entrée. Cependant, géocodage en aval les demandes peuvent renvoyer plusieurs objets de repère dans les situations où le l'adresse spécifiée n'a pas pu être résolue en un seul endroit. Si la la demande a été annulée ou une erreur s’est produite lors de l’obtention du repère information, ce paramètre est nul.
Pour plus d'informations sur les propriétés de CLPlacemark, vous pouvez vérifier ceci CLPlacemark
Usage:
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")
}
}
}
Cela va imprimer
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
Lagoa
Rio de Janeiro RJ
22011-040
Brésil
Il y a tellement de questions portant sur 'reverseGeocodeLocation' sur Stack Overflow, j'en ai vu tellement. J'utilise Swift 4.2 et je pensais que mes requêtes 'reverseGeocodeLocation' pouvaient arriver à expiration. Eh bien, ce n'est pas le cas, lorsque vous commencez à vous déplacer avec un iPhone, les cycles 'didUpdateLocations' doivent mûrir. Deux cycles ou plus peuvent être nécessaires avant que 'reverseGeocodeLocation' ne renvoie des repères.
J'ai écrit une application simple qui utilise "reverseGeocodeLocation" jusqu'à ce que "repères" soit renvoyé après des cycles répétés "didUpdateLocations". Ceci est mon propre 'SampleCode'. J'ai beaucoup appris en utilisant l'application. Cela pourrait aider les autres à comprendre le fonctionnement des demandes de géocodage inversé dans le monde réel.
S'il vous plaît donner vos commentaires sur les améliorations possibles. Ce code est donné et pris librement.
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'"}}}}}}
Et puis l'interface utilisateur du Storyboard:
<?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>