How to extract lat/long placemark.coordinate in Swift? - ios

I added a location search bar to my app. The tableview controller consists of the user location lat/long as well as the destination (address the user inputs into the search bar). As far as I can tell the latitude / longitude is within the placemark.coordinate:
placemark.coordinate = CLLocationCoordinate2D(latitude: 45.253519203737767, longitude: -66.070974382763978)
I have two variables: destLat and destLong. How I can extract the latitude and longitude from placemark.coordinate and place into the two variables mentioned above?
related code:
// cache the pin
selectedPin = placemark
// clear existing pins
mapView.removeAnnotations(mapView.annotations)
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let city = placemark.locality,
let prov = placemark.administrativeArea {
annotation.subtitle = "\(city), \(prov)"
}
let destination = placemark.coordinate
print("Destination coordinates: \(destination)")
let name = placemark.name
print("Placemark Name: \(name!)")
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegionMake(placemark.coordinate, span)
mapView.setRegion(region, animated: true)
Console output:

Perhaps your were thinking too hard. You can assign values to these variables just like any other:
let (destLat, destLong) = (placemark.coordinate.latitude, placemark.coordinate.longitude)

If both variable you have declare is type of Double then you need to simply need to access the latitude and longitude property of CLLocationCoordinate2D.
destLat = destination.latitude
destLong = destination.longitude
If type of both var is different then simply change it to Double works for you.

Related

How to get directions from user location to another specific location?

I have 2 locations in the map. First one is users current location with blue dot placemark and the second one is specified restaurant location near of the users current location with red dot placemark. I want to get directions from user's current location to restaurant location. I am getting directions when I define latitude and longitude values like latitude: 38.00001 and longitude: 27.08980.
Also I am getting each restaurants location from api to red dot placemark but do not get directions from user current location to restaurant location.
I am getting restaurant location with these codes:
var viewModel: DetailsViewModel? {
didSet {
updateView()
}
willSet {
updateView()
}
}
func updateView() {
if let viewModel = viewModel {
detailsFoodView?.priceLabel?.text = viewModel.price
detailsFoodView?.hoursLabel?.text = viewModel.isOpen
detailsFoodView?.locationLabel?.text = viewModel.phoneNumber
detailsFoodView?.ratingsLabel?.text = viewModel.rating
detailsFoodView?.collectionView?.reloadData()
centerMap(for: viewModel.coordinate)
print("detail restaurant destination : \(viewModel.coordinate)")
title = viewModel.name
print("title: \(title)")
}
}
func centerMap(for coordinate: CLLocationCoordinate2D) {
let region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 100, longitudinalMeters: 100)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
detailsFoodView?.mapView?.addAnnotation(annotation)
detailsFoodView?.mapView?.setRegion(region, animated: true)
}
}
And in viewDidLoad() getting directions like:
let sourceCoordinates = locationManager.location?.coordinate
Problem is here
I do not get each restaurant's latitude and longitude values to destCoordinates. Normally, I get with viewModel.coordinate.
But if I use
let destCoordinates = CLLocationCoordinate2D(latitude: viewModel?.coordinate.latitude, longitude: viewModel?.coordinate.longitude)
Instead of:
let destCoordinates = CLLocationCoordinate2D(latitude: 38.420183, longitude: 27.205559)
I am getting latitude and longitude values nil or zero.
let sourcePlaceMark = MKPlacemark(coordinate: sourceCoordinates!)
let destPlaceMark = MKPlacemark(coordinate: destCoordinates)
let sourceItem = MKMapItem(placemark: sourcePlaceMark)
let destItem = MKMapItem(placemark: destPlaceMark)
let directionRequest = MKDirections.Request()
directionRequest.source = sourceItem
directionRequest.destination = destItem
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate(completionHandler: {response, error in
guard let response = response else {
if error != nil {
print("Something went wrong!")
}
return
}
let route = response.routes[0]
self.detailsFoodView?.mapView?.addOverlay(route.polyline, level: .aboveRoads)
let rect = route.polyline.boundingMapRect
self.detailsFoodView?.mapView?.setRegion(MKCoordinateRegion(rect), animated: true)
Expected result is current user place as blue dot placemark to red dot placemark with red line as you can see in the screen shot.
Image:

How do you get a label to be read as a number?

My map annotation works well when physically putting the numbers in, but, how do I use it so,
for example, latitudelabel.text is read as the latitude rather than 38.897957?
Here is the code:
func showEmergenyOnMap() {
let emergency = MKPointAnnotation()
emergency.title = "Ongoing Emergency"
emergency.coordinate = CLLocationCoordinate2D(latitude: 38.897957, longitude: -77.036560)
Map.addAnnotation(emergency)
}
Covert string to double.
let lati = Double(label.text)
// do same for longi
Then init coordinate
let coords = CLLocationCoordinate2D(latitude: lati, longitude: longi)
UILabel's text property is an Optional variable so it can have a value or a nil. First of all you need to safely unwrap that value because CLLocationDegrees initializer takes a non-optional String. You can see the below example on how to convert labels text to CLLocationCoordinate2D,
var latitude: CLLocationDegrees = 0.0
var longitude: CLLocationDegrees = 0.0
if let latText = latitudelabel.text, let lat = CLLocationDegrees(latText) {
latitude = lat
}
if let longText = longitudelabel.text, let long = CLLocationDegrees(longText) {
longitude = long
}
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

Annotation.coordinate does not match coordinate.latitude and coordinate.longitude

In my project I use LocationSearchTable / MKLocalSearch. When a user clicks on an item, my add annotation method is called in MapViewController:
func dropPinZoomIn(placemark: MKPlacemark) {
// cache the pin
selectedPin = placemark
// create the pin
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let streetNumber = placemark.subThoroughfare,
let city = placemark.locality,
let state = placemark.administrativeArea {
annotation.subtitle = "\(streetNumber) \(city) \(state)"
}
// add the pin the the mapView
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.01, 0.01)
let region = MKCoordinateRegionMake(annotation.coordinate, span)
mapView.setRegion(region, animated: true)
print(placemark.coordinate)
print(placemark.coordinate.latitude)
print(placemark.coordinate.longitude)
At the end, when I print the coordinate, I get a longer version whereas when I print the latitude and longitude separately, it rounds off the number near the end. This is causing me troubles later when I need to compare coordinates. How can I prevent this rounding? As an example, here are my results:
CLLocationCoordinate2D(latitude: 37.331413259110334, longitude: -122.03048408031462)
CLLocationCoordinate2D(latitude: 37.3314132591103, . . longitude: -122.030484080315)
Try the following code to prevent rounds off the number.
print(coordinate)
print(coordinate.latitude.debugDescription)
print(coordinate.longitude.debugDescription)
Result:
CLLocationCoordinate2D(latitude: -33.499989999999997, longitude: 150.255)
-33.499989999999997
150.255
Note:
The type of coordinate.latitude/coordinate.longitude is CLLocationDegrees. This type is alias for Double. Comparing Double or Float values with == will not give the expected result in most programming languages, which means that numbers that you think should be equal are in fact slightly different. Instead compute the absolute difference and handle the numbers as equal if the difference is below some threshold. See Compare double to zero using epsilon for more explanations.

Convert String from CoreData into CLLocationDegrees/ CLLocationCoordinate2D

I had been struggling to store CLLocationCoordinate2D data from markers on a google maps to CoreData. This cannot be done directly but I found a work around where I take the coordinates, split into CLLocationDegrees, convert it into a string text and store it. I do this by the following:
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(place.coordinate.latitude, place.coordinate.longitude)
let newPlaceLatitude = place.coordinate.latitude
print(newPlaceLatitude)
var latitudeText:String = "\(newPlaceLatitude)"
self.latitudeText = "\(newPlaceLatitude)"
let newPlaceLongitude = place.coordinate.longitude
print(newPlaceLongitude)
var longitudeText:String = "\(newPlaceLongitude)"
self.longitudeText = "\(newPlaceLongitude)"
Storing into CoreData:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let newPlace = NSEntityDescription.insertNewObject(forEntityName:
"StoredPlace", into: context)
newPlace.setValue(latitudeText, forKeyPath: "latitude")
newPlace.setValue(longitudeText, forKeyPath: "longitude")
However now I am struggling to reconstruct the strings back into CLLocationCoordinates. How would I turn a string to a CLLocationDegree/CLLocationCoordinate2D ? This is supposedly pretty simple but I have found that the following method doesn't work:
let latitude: CLLocationDegrees = Double(latitudeText)!
let longitude: CLLocationDegrees = Double(longitudeText)!
let markers = GMSMarker()
print(latitude)
print(longitude)
markers.position = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
any other suggestions about how to change string to coordinate?
CLLocation's latitude and longitude are doubles, so with that in mind, you might consider having latitude and longitude properties that are doubles on your StoredPlace object. I called the properties coordinateX and coordinateY so it's easier to remember that they're custom coordinates, not "factory" properties.
You could create an extension in a file called StoredPlace+Extension.swift that looks like this:
import CoreData
import CoreLocation
extension StoredPlace {
func location() -> CLLocation {
let location = CLLocation(latitude: self.coordinateX, longitude: self.coordinateY)
return location
}
}
With this extension, you can then get the coordinates out of your results as follows:
for result in results {
print("coordinate = \(result.location().coordinate)")
print("latitude = \(result.location().coordinate.latitude)")
print("longitude = \(result.location().coordinate.longitude)")
}
You need to typecast your lat and long in decimal values, more preferable is double instead of float because of precision value which can drop pins at perfect locations.
Type casting in double using as keyword:
(yourCordinateString as NSString).doubleValue
casting in float values:
(yourCordinateString as NSString).floatValue

Checking for parameters for CLLocationCoordinate2DMake

I have a mapview where I want to check if latitude og longitude values are empty/nil or not, but either the app crashes, or Xcode is complaining about optional types and what not.
This is my current code, but I want to find out what the best way to get correct format for latitude and longitude coordinates:
if let latitude = detailItem!["lat"] as? Double {
if let longitude = detailItem!["lon"] as? Double {
// Get location
let location = CLLocationCoordinate2DMake(latitude, longitude)
// Set region
let region = MKCoordinateRegionMakeWithDistance(location, 2000, 2000)
mapCell.mapView.setRegion(region, animated: false)
// Add pin to map
let marker = MKPointAnnotation()
marker.coordinate = location
marker.title = String(detailItem!["name"]!)
mapCell.mapView.addAnnotation(marker)
}
}
This crashes entries with values.

Resources