将坐标转换为城市名称?
Posted
技术标签:
【中文标题】将坐标转换为城市名称?【英文标题】:Convert coordinates to City name? 【发布时间】:2015-01-01 23:52:59 【问题描述】:如何使用 MapKit 从坐标中获取地址?
我有这个代码,当长按地图时它会得到坐标:
func didLongPressMap(sender: UILongPressGestureRecognizer)
if sender.state == UIGestureRecognizerState.Began
let touchPoint = sender.locationInView(self.mapView)
let touchCoordinate = self.mapView.convertPoint(touchPoint, toCoordinateFromView: self.mapView)
var annotation = MKPointAnnotation()
annotation.coordinate = touchCoordinate
annotation.title = "Your position"
self.mapView.addAnnotation(annotation) //drops the pin
println("lat: \(touchCoordinate.latitude)")
var num = (touchCoordinate.latitude as NSNumber).floatValue
var formatter = NSNumberFormatter()
formatter.maximumFractionDigits = 4
formatter.minimumFractionDigits = 4
var str = formatter.stringFromNumber(num)
println("long: \(touchCoordinate.longitude)")
var num1 = (touchCoordinate.longitude as NSNumber).floatValue
var formatter1 = NSNumberFormatter()
formatter1.maximumFractionDigits = 4
formatter1.minimumFractionDigits = 4
var str1 = formatter1.stringFromNumber(num1)
self.adressLoLa.text = "\(num),\(num1)"
我想在annotation.title
中打印完整地址(街道、城市、邮编、国家/地区)。
【问题讨论】:
【参考方案1】:SWIFT 4.2:编辑
MapKit
框架确实提供了一种从坐标中获取地址详细信息的方法。
您需要使用地图套件的反向地理编码。 CLGeocoder
类用于从地址获取位置,从位置(坐标)获取地址。 reverseGeocodeLocation
方法会从坐标返回地址详情。
该方法接受CLLocation
作为参数并返回CLPlacemark
,其中包含地址字典。
所以现在上面的方法将更新为:
@objc func didLongPressMap(sender: UILongPressGestureRecognizer)
if sender.state == UIGestureRecognizer.State.began
let touchPoint = sender.location(in: mapView)
let touchCoordinate = mapView.convert(touchPoint, toCoordinateFrom: self.mapView)
let annotation = MKPointAnnotation()
annotation.coordinate = touchCoordinate
annotation.title = "Your position"
mapView.addAnnotation(annotation) //drops the pin
print("lat: \(touchCoordinate.latitude)")
let num = touchCoordinate.latitude as NSNumber
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 4
formatter.minimumFractionDigits = 4
_ = formatter.string(from: num)
print("long: \(touchCoordinate.longitude)")
let num1 = touchCoordinate.longitude as NSNumber
let formatter1 = NumberFormatter()
formatter1.maximumFractionDigits = 4
formatter1.minimumFractionDigits = 4
_ = formatter1.string(from: num1)
self.adressLoLa.text = "\(num),\(num1)"
// Add below code to get address for touch coordinates.
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)
geoCoder.reverseGeocodeLocation(location, completionHandler:
placemarks, error -> Void in
// Place details
guard let placeMark = placemarks?.first else return
// Location name
if let locationName = placeMark.location
print(locationName)
// Street address
if let street = placeMark.thoroughfare
print(street)
// City
if let city = placeMark.subAdministrativeArea
print(city)
// Zip code
if let zip = placeMark.isoCountryCode
print(zip)
// Country
if let country = placeMark.country
print(country)
)
【讨论】:
使用地址键的条件解包而不是两次提取键值不是更好吗? 我不是这个意思。应该是if let locationName = placeMark.addressDictionary["Name"] println(locationName)
@Abizern 你的意思是用字符串初始化键? (使用条件运算符)对吗?
为什么要转换成 NSString?您所做的只是打印结果。
这是因为如果我们不投射它,那么它将打印Optional(city)
。所以它只是为了删除这个烦人的可选关键字。【参考方案2】:
对于 Swift 3: 和 Swift 4
首先您需要在info.plist
中设置接收用户 GPS 的限额。
使用随机字符串设置:NSLocationWhenInUseUsageDescription
。
和/或:NSLocationAlwaysUsageDescription
带有随机字符串。
然后我设置了一个类来获取所需的数据,例如 zip、城镇、国家...:
import Foundation
import MapKit
typealias JSONDictionary = [String:Any]
class LocationServices
let shared = LocationServices()
let locManager = CLLocationManager()
var currentLocation: CLLocation!
let authStatus = CLLocationManager.authorizationStatus()
let inUse = CLAuthorizationStatus.authorizedWhenInUse
let always = CLAuthorizationStatus.authorizedAlways
func getAdress(completion: @escaping (_ address: JSONDictionary?, _ error: Error?) -> ())
self.locManager.requestWhenInUseAuthorization()
if self.authStatus == inUse || self.authStatus == always
self.currentLocation = locManager.location
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(self.currentLocation) placemarks, error in
if let e = error
completion(nil, e)
else
let placeArray = placemarks as? [CLPlacemark]
var placeMark: CLPlacemark!
placeMark = placeArray?[0]
guard let address = placeMark.addressDictionary as? JSONDictionary else
return
completion(address, nil)
调用者:
import UIKit
class ViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
LocationServices.shared.getAdress address, error in
if let a = address, let city = a["City"] as? String
//
完成
【讨论】:
谢谢,一个很好的解决方案。我做了一些改动,currentLocation 是空的,所以我从 viewController 传递了它。【参考方案3】:import Foundation
import CoreLocation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
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 streetNumber: String // eg. 1
let streetName: String // eg. Infinite Loop
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 ?? ""
旧的/已弃用的答案:
感谢@Kampai's answer,这是一种兼容Swift 3且更安全的方式(不强制!
):
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)
geoCoder.reverseGeocodeLocation(location, completionHandler: placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else
return
// Print each key-value pair in a new row
addressDict.forEach print($0)
// Print fully formatted address
if let formattedAddress = addressDict["FormattedAddressLines"] as? [String]
print(formattedAddress.joined(separator: ", "))
// Access each element manually
if let locationName = addressDict["Name"] as? String
print(locationName)
if let street = addressDict["Thoroughfare"] as? String
print(street)
if let city = addressDict["City"] as? String
print(city)
if let zip = addressDict["ZIP"] as? String
print(zip)
if let country = addressDict["Country"] as? String
print(country)
)
别忘了NSLocationWhenInUseUsageDescription
和NSLocationAlwaysUsageDescription
键
【讨论】:
【参考方案4】:感谢@Kampi。这是更新的 Swift 2.0 (Xcode 7) 版本:
func setUsersClosestCity()
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: _point1.coordinate.latitude, longitude: _point1.coordinate.longitude)
geoCoder.reverseGeocodeLocation(location)
(placemarks, error) -> Void in
let placeArray = placemarks as [CLPlacemark]!
// Place details
var placeMark: CLPlacemark!
placeMark = placeArray?[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)
【讨论】:
你有没有遇到过多次运行这个循环的问题?我的代码有问题,它会在停止之前输出我的城市名称三次,这会导致我的应用程序出现问题。 my SO post/question is here【参考方案5】:感谢@Kampai 的回答,我稍作修改,使其适用于Swift 1.2
:
var geocoder = CLGeocoder()
var location = CLLocation(latitude: IC.coordinate!.latitude, longitude: IC.coordinate!.longitude)
geocoder.reverseGeocodeLocation(location)
(placemarks, error) -> Void in
if let placemarks = placemarks as? [CLPlacemark] where placemarks.count > 0
var placemark = placemarks[0]
println(placemark.addressDictionary)
结果:
[
SubLocality: Sydney,
Street: 141 Harrington Street,
State: NSW,
SubThoroughfare: 141,
CountryCode: AU, ZIP: 2000,
Thoroughfare: Harrington Street,
Name: 141 Harrington Street,
Country: Australia, FormattedAddressLines: (
"141 Harrington Street",
"The Rocks NSW 2000",
Australia
),
City: The Rocks
]
【讨论】:
感谢 superarts.org。【参考方案6】:斯威夫特 4.2 尽量保持简单,看苹果doc,根据需要修改:
func retreiveCityName(lattitude: Double, longitude: Double, completionHandler: @escaping (String?) -> Void)
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler:
placeMarks, error in
completionHandler(placeMarks?.first?.locality)
)
【讨论】:
【参考方案7】:更新Swift 4
addressDictionary 在 ios 11.0 中为 deprecated
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: 37.769193, longitude: -122.426512)
geoCoder.reverseGeocodeLocation(location, completionHandler: (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// Complete address as PostalAddress
print(placeMark.postalAddress as Any) // Import Contacts
// Location name
if let locationName = placeMark.name
print(locationName)
// Street address
if let street = placeMark.thoroughfare
print(street)
// Country
if let country = placeMark.country
print(country)
)
可以检索更多数据
名称、通道、subThoroughfare、地点、subLocality、administrativeArea、subAdministrativeArea、postalcode、isoCountryCode、country、inlandWater、areaOfInterest
【讨论】:
为了通过这种方法获取城市名称,您必须使用 placeMark.locality,感谢它对我有用 是的。在描述中查找更多信息。【参考方案8】:在 didUpdateToLocation 方法中:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:
(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
CLLocation *location = [locationManager location];
CLLocationCoordinate2D coordinate = [location coordinate];
latitude = [NSString stringWithFormat:@"%.12f", coordinate.latitude];
longitude = [NSString stringWithFormat:@"%.12f", coordinate.longitude];
CLLocation *location1 = [[CLLocation alloc]
initWithLatitude:latitude.floatValue
longitude:longitude.floatValue];
self.myGeocoder = [[CLGeocoder alloc] init];
[self.myGeocoder
reverseGeocodeLocation:location1
completionHandler:^(NSArray *placemarks, NSError *error)
if (error == nil &&
[placemarks count] > 0)
placemark = [placemarks lastObject];
NSString* vendorLocation=[NSString stringWithFormat:@"%@ %@",
placemark.locality,
placemark.subLocality];
NSLog(@"%@",vendorLocation);
];
【讨论】:
【参考方案9】: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))")
)
使用此代码将解决问题。
【讨论】:
以上是关于将坐标转换为城市名称?的主要内容,如果未能解决你的问题,请参考以下文章