draw polyline between two latitude and longitude - ios

I pass two lat and long for source and destination but not draw poly line between two point.always get something wrong error .below code I pass my current location in source coordinate and travel location pass in destCoordinates. I want draw poly line between sourceCoordinates to destCoordinates..
if(CLLocationManager.locationServicesEnabled()){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
//let sourceCoordinates = locationManager.location?.coordinate
let sourceCoordinates = CLLocationCoordinate2DMake(19.115950,72.856448)
let destCoordinates = CLLocationCoordinate2DMake(19.119670,72.853616)
let sourcePlacemark = MKPlacemark(coordinate: sourceCoordinates)
let destPlacemark = MKPlacemark(coordinate: destCoordinates)
let sourceItem = MKMapItem(placemark: sourcePlacemark)
let destItem = MKMapItem(placemark: destPlacemark)
let directionRequest = MKDirectionsRequest()
directionRequest.source=sourceItem
directionRequest.destination=destItem
directionRequest.transportType = .walking
let directions = MKDirections(request: directionRequest)
directions.calculate(completionHandler: {
response,error in
guard let response = response else{
if let error = error {
print("something wrong")
}
return
}
let route = response.routes[0]
self.upcoming_info_map.add(route.polyline,level: .aboveRoads)
let rekt = route.polyline.boundingMapRect
self.upcoming_info_map.setRegion(MKCoordinateRegionForMapRect(rekt), animated: true)
})
for draw polyline i use this function
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolygonRenderer(overlay:overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 5.0
return renderer
}

use this method.takes list of locations and draw polyline
func addPolyLineToMap(googlemaplist: [CLLocation?]){
var coordinates = googlemaplist.map({ (location: CLLocation!) -> CLLocationCoordinate2D in
return location.coordinate
})
print("locatios count")
print(googlemaplist.count)
var polyline = MKPolyline(coordinates: &coordinates, count: googlemaplist.count)
DispatchQueue.main.async {
self.MapKit.add(polyline)
}
}

Related

How to streamline multiple coordinate poly line entries in MKMapKit

I am trying to add multiple poly lines onto a map using mkmapkit. These poly lines indicate walking zones in my area. The problem is that my code is too bulky for a large amount of walking zones.
At the moment my code only indicates 2 walking routes but for instance if I want to add 100 or 1000 walking routes the code would be massive. I'm sure there is a way I could stream line this code so I could add in multiple walking zones with a lot less code but in not too sure the best way to go about it.
import UIKit
import MapKit
class customPin: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(pinTitle:String, pinSubTitle:String, location:CLLocationCoordinate2D) {
self.title = pinTitle
self.subtitle = pinSubTitle
self.coordinate = location
}
}
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//co-ordinates
let zone1S = CLLocationCoordinate2D(latitude: 52.100525, longitude: -9.623071)
let zone1E = CLLocationCoordinate2D(latitude: 52.07241, longitude: -9.575299)
let zone2S = CLLocationCoordinate2D(latitude: 52.054161, longitude: -9.385031)
let zone2E = CLLocationCoordinate2D(latitude: 52.081185, longitude: -9.247033)
//pins
let zone1PinS = customPin(pinTitle: "Zone 1 Start", pinSubTitle: "", location: zone1S)
let zone1PinE = customPin(pinTitle: "Zone 1 End", pinSubTitle: "", location: zone1E)
self.mapView.addAnnotation(zone1PinS)
self.mapView.addAnnotation(zone1PinE)
let zone2PinS = customPin(pinTitle: "Zone 2 Start", pinSubTitle: "", location: zone2S)
let zone2PinE = customPin(pinTitle: "Zone 2 End", pinSubTitle: "", location: zone2E)
self.mapView.addAnnotation(zone2PinS)
self.mapView.addAnnotation(zone2PinE)
let zone1PlacemarkS = MKPlacemark(coordinate: zone1S)
let zone1PlacemarkE = MKPlacemark(coordinate: zone1E)
let zone2PlacemarkS = MKPlacemark(coordinate: zone2S)
let zone2PlacemarkE = MKPlacemark(coordinate: zone2E)
//add polyline to map
let directionRequestZone1 = MKDirections.Request()
directionRequestZone1.source = MKMapItem(placemark: zone1PlacemarkS)
directionRequestZone1.destination = MKMapItem(placemark: zone1PlacemarkE)
let directionRequestZone2 = MKDirections.Request()
directionRequestZone2.source = MKMapItem(placemark: zone2PlacemarkS)
directionRequestZone2.destination = MKMapItem(placemark: zone2PlacemarkE)
//type of commute
directionRequestZone1.transportType = .automobile
directionRequestZone2.transportType = .automobile
let directions1 = MKDirections(request: directionRequestZone1)
directions1.calculate { (response, error) in
guard let directionResonse = response else {
if let error = error {
print("we have error getting directions==\(error.localizedDescription)")
}
return
}
let route = directionResonse.routes[0]
self.mapView.addOverlay(route.polyline, level: .aboveRoads)
let rect = route.polyline.boundingMapRect
//zooming in on location
// self.mapView.setRegion(MKCoordinateRegion(rect), animated: true)
}
let directions2 = MKDirections(request: directionRequestZone2)
directions2.calculate { (response, error) in
guard let directionResonse = response else {
if let error = error {
print("we have error getting directions==\(error.localizedDescription)")
}
return
}
let route2 = directionResonse.routes[0]
self.mapView.addOverlay(route2.polyline, level: .aboveRoads)
let rect = route2.polyline.boundingMapRect
//zooming in on location
// self.mapView.setRegion(MKCoordinateRegion(rect), animated: true)
}
//set delegate for mapview
self.mapView.delegate = self
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 5.0
return renderer
}
}
This is how it looks:
UPDATE:
My attempt to use loops hasn't worked. I seem to have a loop working for dropping the start and end zone pins however it doesn't seem to work for drawing the poly lines.
struct Location {
let title: String
let latitude: Double
let longitude: Double
}
let locationsStart = [
Location(title: "Start", latitude: 52.100525, longitude: -9.623071)
]
let locationsEnd = [
Location(title: "End", latitude: 52.07241, longitude: -9.575299)
]
for location in locationsStart {
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
mapView.addAnnotation(annotation)
let directionRequestZone1 = MKDirections.Request()
let zonePlacemarkS = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(placemark: zonePlacemarkS)
directionRequestZone1.transportType = .automobile
}
for location in locationsEnd {
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
mapView.addAnnotation(annotation)
let directionRequestZone1 = MKDirections.Request()
let zonePlacemarkE = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude))
let directionRequest = MKDirections.Request()
directionRequest.destination = MKMapItem(placemark: zonePlacemarkE)
directionRequestZone1.transportType = .automobile
let directions1 = MKDirections(request: directionRequestZone1)
directions1.calculate { (response, error) in
guard let directionResonse = response else {
if let error = error {
print("we have error getting directions==\(error.localizedDescription)")
}
return
}
let route = directionResonse.routes[0]
self.mapView.addOverlay(route.polyline, level: .aboveRoads)
let rect = route.polyline.boundingMapRect
}
}
//set delegate for mapview
self.mapView.delegate = self
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 5.0
return renderer
}
I adapted your code as follows:
let zone1S = CLLocationCoordinate2D(latitude: 52.100525, longitude: -9.623071)
let zone1E = CLLocationCoordinate2D(latitude: 52.07241, longitude: -9.575299)
let zone2S = CLLocationCoordinate2D(latitude: 52.054161, longitude: -9.385031)
let zone2E = CLLocationCoordinate2D(latitude: 52.081185, longitude: -9.247033)
let zones = [(start:zone1S, end:zone1E), (start:zone2S, end:zone2E)]
for (i, zone) in zones.enumerated() {
let pinS = customPin(pinTitle: "Zone \(i+1) Start", pinSubTitle: "", location: zone.start)
let pinE = customPin(pinTitle: "Zone \(i+1) End", pinSubTitle: "", location: zone.end)
self.mapView.addAnnotation(pinS)
self.mapView.addAnnotation(pinE)
let placeS = MKPlacemark(coordinate: zone.start)
let placeE = MKPlacemark(coordinate: zone.end)
let req = MKDirections.Request()
req.source = MKMapItem(placemark: placeS)
req.destination = MKMapItem(placemark: placeE)
req.transportType = .automobile
let dir = MKDirections(request: req)
dir.calculate { (response, error) in
guard let directionResponse = response else {
if let error = error {
print("we have error getting directions==\(error.localizedDescription)")
}
return
}
DispatchQueue.main.async {
let route = directionResponse.routes[0]
self.mapView.addOverlay(route.polyline, level: .aboveRoads)
}
}
}
Clearly this can be trivially extended to any number of zones. (It may be that we should be using something like a DispatchGroup to prevent the networking calculate calls from piling up too quickly, but the goal was to solve it for two pairs of coordinates and this seems to work fine.)

MKDirections : Directions Not Available

I am creating an app like an UBER and i am using MapKit ,i want to display route between two locations and for this i have use following code
viewMap.delegate = self
let sourceLocation = CLLocationCoordinate2D(latitude: sourceLatitude, longitude: sourceLongitude)
let destinationLocation = CLLocationCoordinate2D(latitude: destinationLatitude, longitude: destinationLongitude)
let sourcePlacemark = MKPlacemark(coordinate: sourceLocation, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
let sourceAnnotation = MKPointAnnotation()
sourceAnnotation.title = strSource
if let location = sourcePlacemark.location {
sourceAnnotation.coordinate = location.coordinate
}
let destinationAnnotation = MKPointAnnotation()
destinationAnnotation.title = strDestination
if let location = destinationPlacemark.location {
destinationAnnotation.coordinate = location.coordinate
}
self.viewMap.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true )
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Route Error: \(error)")
}
return
}
let route = response.routes[0]
self.viewMap.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.viewMap.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
extension HomeVC: MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
renderer.lineWidth = 2.0
return renderer
}
I have check all the possible solution and i am also getting latitude and longitude of source and destination Locations but i still its not working and i am getting following error.
Please help me with this!
Apple maps directions is available to specific countries only. Please check full list here. Apple Map Availability list (Check in Maps: Directions section). You should look into Google Map SDK in your app is targeting countries not listed in above link.
This works for me and it draws the destination in the MK Map, also it focuses to the destination
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let request = MKDirectionsRequest()
request.source = MKMapItem.forCurrentLocation()
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: (view.annotation?.coordinate)!))
request.requestsAlternateRoutes = true
request.requestsAlternateRoutes = true
request.transportType = .walking
let directions = MKDirections(request: request)
directions.calculate { [unowned self] response, error in
guard let unwrappedResponse = response else { return }
for route in unwrappedResponse.routes {
self.myMapView.add(route.polyline) self.myMapView.setVisibleMapRect(route.polyline.boundingMapRect, animated: true)
return
}
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let polyLine = overlay
let polyLineRenderer = MKPolylineRenderer(overlay: polyLine)
polyLineRenderer.strokeColor = UIColor.blue
polyLineRenderer.lineWidth = 2.0
return polyLineRenderer
}
this works when you click on a MKAnnotationView, if you want to draw from another code you just have to cut the code to your function and change the line
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: (view.annotation?.coordinate)!))
to
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: (CLLocationCoordinate2D(/*Your Location*/))!))

MapKit Direction to Selected annotation

Trying to get direction to selected annotation on the map from the current location of the User but not sure how to catch selected annotation(when user selecting one of them) and show direction.Checked different topics on the Stack but there is not a lot of fresh information about how catch selected annotation and show direction for user location.Please advise.
import UIKit
import MapKit
import CoreLocation
class NavigationVC: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let manager = CLLocationManager()
let request = MKLocalSearchRequest()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
}
//MARK: CONFIGURATION OF MAPVIEW
extension NavigationVC: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations
locations: [CLLocation]) {
let location = locations[0]
let span: MKCoordinateSpan = MKCoordinateSpanMake(0.1, 0.1 )
let userLocation:CLLocationCoordinate2D =
CLLocationCoordinate2DMake(location.coordinate.latitude,
location.coordinate.longitude)
let region: MKCoordinateRegion =
MKCoordinateRegionMake(userLocation, span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
request.naturalLanguageQuery = "Currency exchange"
request.region = mapView.region
let activeSearch = MKLocalSearch(request: request)
activeSearch.start { (response, error) in
guard let response = response else {
return
}
for item in response.mapItems {
let annotation = MKPointAnnotation()
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name
DispatchQueue.main.async {
self.mapView.addAnnotation(annotation)
}
}
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) ->
MKOverlayRenderer {
let polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.blue
polylineRenderer.fillColor = UIColor.red
polylineRenderer.lineWidth = 2
return polylineRenderer
}
}
To get the path on the map from your position to the annotation follow this code:
annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(lat),longitude: CLLocationDegrees(lon))
self.map?.addAnnotation(annotation)
var route : MKRoute? = nil
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
let directionRequest = MKDirectionsRequest()
directionRequest.source = MKMapItem.forCurrentLocation();
directionRequest.destination = MKMapItem(placemark: MKPlacemark(coordinate:(self?.annotation.coordinate)!))
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
route = response.routes[0]
if let percorso = route{
DispatchQueue.main.async { [weak self] in
self?.map?.add((percorso.polyline), level: MKOverlayLevel.aboveRoads)
}
}
}
}
You have only to select the correct annotation, maybe try to register a touch listener on the annotation.
Use didSelect delegate method.
e.g.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let annotation = view.annotation else {
return
}
let directionRequest = MKDirectionsRequest()
directionRequest.source = MKMapItem.forCurrentLocation()
directionRequest.destination = MKMapItem(placemark: MKPlacemark(coordinate: annotation.coordinate))
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
if !response.routes.isEmpty {
let route = response.routes[0]
DispatchQueue.main.async { [weak self] in
self?.mapView.add(route.polyline)
}
}
}
}
Don't forget guard to prevent crash.
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) ->
MKOverlayRenderer {
guard overlay is MKPolyline else {
return MKPolylineRenderer()
}
...
}
Good luck!

Swift 3 - UIView loads every time when adding a location coordinate

I am using firebase as my backend. In this application I have used mapkit and firebase. I want to track the location of user. But when I am doing the location of user is sucessfully uploaded to firebase but in my application the view is reloaded.
my code:-
#IBOutlet weak var map:MKMapView!
let locationManager = CLLocationManager()
var lat = [Double]()
var lon = [Double]()
var ref:DatabaseReference!
var uid = ""
override func viewDidLoad() {
super.viewDidLoad()
map.delegate = self
locationManager.delegate = self
ref = Database.database().reference().child("users").child(uid)
ref.child("locations").queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
print("func snapshot \(snapshot)")
let dict = snapshot.value as! [String:Double]
let lat = dict["latitude"]
let lon = dict["longitude"]
print("latidue = \(lat!)")
print("longitude = \(lon!)")
self.lat.append(lat!)
self.lon.append(lon!)
})
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let location = CLLocationCoordinate2D(latitude: lat.first!,longitude: lon.first!)
let destinationLocation = CLLocationCoordinate2D(latitude: lat.last!, longitude:lon.last!)
let sourcePlacemark = MKPlacemark(coordinate: location, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
let sourceAnnotation = MKPointAnnotation()
if let location = sourcePlacemark.location {
sourceAnnotation.coordinate = location.coordinate
}
let destinationAnnotation = MKPointAnnotation()
if let location = destinationPlacemark.location {
destinationAnnotation.coordinate = location.coordinate
}
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.map.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.map.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
map.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = location
map.addAnnotation(annotation)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 4.0
return renderer
}
I don't want to reload my whole view just want to render my mapview whenever new cordinate is uploaded.
Remove code from viewDidAppear and put it inside the observe childAdded callback

launch a function for the map on screen load. iOS, Swift

I am trying to launch the map automatically when the screen loads.
Can this be done? Launch this function on view did appear.
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if let anno = view.annotation {
let place = MKPlacemark(coordinate: anno.coordinate)
let destination = MKMapItem(placemark: place)
destination.name = "Vehicle Sighting"
let regionDistance: CLLocationDistance = 1000
let regionSpan = MKCoordinateRegionMakeWithDistance(anno.coordinate, regionDistance, regionDistance)
let options = [MKLaunchOptionsMapCenterKey: NSValue (mkCoordinate: regionSpan.center), MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span), MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving] as [String : Any]
MKMapItem.openMaps(with: [destination], launchOptions: options)
}
}
Try with this function, you only need to pass the coordinates and place name, this works, I use this in several projects
static func openMapsAppWithLocation(coordinates:CLLocationCoordinate2D,placeName:String)
{
let regionDistance:CLLocationDistance = 10000
let regionSpan = MKCoordinateRegionMakeWithDistance(coordinates, regionDistance, regionDistance)
let options = [
MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center),
MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span)
]
let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = placeName
mapItem.openInMaps(launchOptions: options)
}
Hope this helps
an alternative version using #Reinier answer.
this shows how it was implemented into an action the view controller before with no need to go to the next controller to launch in view did load. Also how i got the coordinates from a postcode
var addressCode = [[String: Double]]()
#IBAction func onMapPressed(_ sender: Any) {
let address = postCode
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) {
placemarks, error in
let placemark = placemarks?.first
let lat = placemark?.location?.coordinate.latitude
let lon = placemark?.location?.coordinate.longitude
// let locationTitle = self.places]
let latLon = ["latitude":lat!, "longitude": lon!]
self.addressCode.append(latLon)
for location in self.addressCode {
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: location["latitude"]!, longitude: location["longitude"]!)
self.openMapsAppWithLocation(coordinates: annotation.coordinate, placeName: self.makeLabel.text!)
}
}
}

Resources