SWIFT 3.0
MKMAPVIEW
iOS
Note : - (Integrated AppleMap ,Not working with GoogleMap)
I have done the following :
Implemented map and Added custom Image to User Location Annotation
When map open , it shows User Location at right Place
My Requirement :
When User Move into different direction staying at same place (or
different place) the pin (at current location) should automatically
point the direction in which user points.
E.g : If Boat is showing at User Location position and its pointing toward North but if user move toward West then boat (User Location Pin) also should point to that direction.
Tried with following Code :
//MARK:Change Direction Methods
func angle(fromCoordinate first: CLLocationCoordinate2D, toCoordinate second: CLLocationCoordinate2D) -> Float {
let deltaLongitude = second.longitude - first.longitude
let deltaLatitude = second.latitude - first.latitude
let angle = (.pi * 0.5) - atan(deltaLatitude / deltaLongitude)
if deltaLongitude > 0 {
return Float(angle)
}
else if deltaLongitude < 0 {
return Float(angle) + .pi
}
else if deltaLatitude < 0 {
return .pi
}
return 0.0
}
//Animate direction of User Location
func animateUserLocation() {
//Old Coordinate (PLAT - Previous Lat , PLON - Previous Long)
let oldLocation = CLLocationCoordinate2D(latitude: UserDefaults.standard.value(forKey: "PLAT") as! CLLocationDegrees, longitude: UserDefaults.standard.value(forKey: "PLON") as! CLLocationDegrees)
//New Coordinate (PLAT - Current Lat , PLON - Current Long)
let newLocation = CLLocationCoordinate2D(latitude: UserDefaults.standard.value(forKey: "LAT") as! CLLocationDegrees, longitude: UserDefaults.standard.value(forKey: "LON") as! CLLocationDegrees)
let getAngle = angle(fromCoordinate:oldLocation, toCoordinate:newLocation)
var myAnnotation : RestaurantAnnotation?
if annotationArray.count > 0{
myAnnotation = annotationArray[0]
}
else {
return
}
UIView.animate(withDuration: 2, animations: {() -> Void in
myAnnotation?.coordinate = newLocation
let annotationView = self.map.view(for: myAnnotation!)
annotationView?.transform = CGAffineTransform(rotationAngle: CGFloat(getAngle))
})
//Save Previous lat long
UserDefaults.standard.set(UserDefaults.standard.value(forKey: "LAT"), forKey: "PLAT")
UserDefaults.standard.set(UserDefaults.standard.value(forKey: "LON"), forKey: "PLON")
UserDefaults().synchronize()
}
Called animateUserLocation method from didUpdateLocation Method but no Luck.
Kindly share your suggestion what i am doing wrong . Thanks in advance.
Understanding iOS's location concepts completely helps to resolve any issue regarding AppleMap.
To move map Pin or AnnotationView , we can use the CoreLocation framework which outputs data related to the users current location.
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.startUpdatingHeading()
}
func locationManager(manager: CLLocationManager!, didUpdateHeading heading: CLHeading!) {
// This will print out the direction the device is heading
println(heading.magneticHeading) }
}
}
In the above example, "heading.magneticHeading" will output a value representing the direction the device is pointed at.
0 means north
90 means east
180 means south
270 means west
everything else in between
The next step is to use those values and rotate your AnnotationView or Pin accordingly.
CGAffineTransformMakeRotation can help with this.
For example if you want to rotate to imageview to point to northeast, which would require a degree value of 45, your code might look something like this.
float degrees = 45
imageView.transform = CGAffineTransformMakeRotation(degrees * M_PI/180)
Just be aware that CGAffineTransformMakeRotation() expects a radian value, in the example above we've converted degrees to radians by multiplying degrees with the number of half circles.
Following links really helpful :
https://stackoverflow.com/a/7634232/3400991
Finally Resolved my issue. Hope this complete answer helps other too.
Related
I have an application where I calculate distance travelled like the Uber application. When a driver starts a trip, the location begins to change even though a start point has been specified in the search for a ride, a driver could decide to pass an alternative route or pass long places and routes because he/ she does not know the shortest route, how then do I calculate the total distance.
The starting location is the location the driver hits start button
The end location is the location the driver hits stop button
this is my code so far
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
lastLocation = locations.last!
endTrip(locations.last)
if !hasSetInitialLocation {
let camera = GMSCameraPosition.camera(withTarget: lastLocation!.coordinate, zoom: 17)
self.mapView.animate(to: camera)
hasSetInitialLocation = true
endTrip(lastLocation)
MqttManager.instance.connectToServer()
}
}
func endTrip(endLoaction: CLLocation) {
guard let statusChange = source.getStatusChange() else{return}
var distanceTraveled: Double = 0.0
let initialLocation = CLLocation(latitude: (statusChange.meta?.location?.lat)!, longitude: (statusChange.meta?.location?.lng)!)
let distance = initialLocation.distance(from: endLoaction)
distanceTraveled += distance
let distanceInKM = Utility.convertCLLocationDistanceToKiloMeters(targetDistance: distanceTraveled)
}
How can i calculate the distance to reflect the total distance moved by the driver since there could be a change in route from the proposed start point and end point.
The driver hits a button called start trip, I want to get the distance from that moment till the moment he hits the button end trip
this implementation could be got from a similar working code like these but the only difference is that their is a start button which passes the coordinates at that point and a stop coordinate which is the end of the coordinate.
enum DistanceValue: Int {
case meters, miles
}
func calculateDistanceBetweenLocations(_ firstLocation: CLLocation, secondLocation: CLLocation, valueType: DistanceValue) -> Double {
var distance = 0.0
let meters = firstLocation.distance(from: secondLocation)
distance += meters
switch valueType {
case .meters:
return distance
case .miles:
let miles = distance
return miles
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if startLocation == nil {
startLocation = locations.first
} else if let location = locations.last {
runDistance += lastLocation.distance(from: location)
let calc = calculateDistanceBetweenLocations(lastLocation, secondLocation: location, valueType: .meters)
print("TOTAL LOC 1 \(calc)")
print("TOTAL LOC 2 \(runDistance)")
}
lastLocation = locations.last
}
as shown in my print statements print("TOTAL LOC 1 \(calc)")
print("TOTAL LOC 2 \(runDistance)") how can I make
calc the same with runDistance
here is what is printed in the console
TOTAL LOC 10.29331530774379
TOTAL LOC 2 10.29331530774379
TOTAL LOC 2.2655118031831587
TOTAL LOC 2 12.558827110926948
If you get the distance like this using the first and last coordinate it always returns the wrong value because it can't identify the actual traveling path.
I did resolve the same issue with using the following code.
use GoogleMaps
> pod 'GoogleMaps'
Make the coordinates array while the driver is moving on a route.
var arr = [Any]()
// Driving lat long co-ordinateds continues add in this array according to your expectation either update location or perticuler time duration.
// make GMSMutablePath of your co-ordinates
let path = GMSMutablePath()
for obj in arr{
print(obj)
if let lat = (obj as? NSDictionary)?.value(forKey: PARAMETERS.LET) as? String{
path.addLatitude(Double(lat)!, longitude: Double(((obj as? NSDictionary)?.value(forKey: PARAMETERS.LONG) as? String)!)!)
}
}
print(path) // Here is your traveling path
let km = GMSGeometryLength(path)
print(km) // your total traveling distance.
I did it in this app and it's working fine.
Hope it will helps you :)
OR without GoogleMaps
You have to come with locations, an array of CLLocationCoordinate2D, for yourself, as per your code, though.
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
// MARK: - Variables
let locationManager = CLLocationManager()
// MARK: - IBOutlet
#IBOutlet weak var mapView: MKMapView!
// MARK: - IBAction
#IBAction func distanceTapped(_ sender: UIBarButtonItem) {
let locations: [CLLocationCoordinate2D] = [...]
var total: Double = 0.0
for i in 0..<locations.count - 1 {
let start = locations[i]
let end = locations[i + 1]
let distance = getDistance(from: start, to: end)
total += distance
}
print(total)
}
func getDistance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) -> CLLocationDistance {
// By Aviel Gross
// https://stackoverflow.com/questions/11077425/finding-distance-between-cllocationcoordinate2d-points
let from = CLLocation(latitude: from.latitude, longitude: from.longitude)
let to = CLLocation(latitude: to.latitude, longitude: to.longitude)
return from.distance(from: to)
}
}
Output
A simple function to calculate distance (in meters) given an array of CLLocationCoordinate2D. Uses reduce instead of array iteration.
func computeDistance(from points: [CLLocationCoordinate2D]) -> Double {
guard let first = points.first else { return 0.0 }
var prevPoint = first
return points.reduce(0.0) { (count, point) -> Double in
let newCount = count + CLLocation(latitude: prevPoint.latitude, longitude: prevPoint.longitude).distance(
from: CLLocation(latitude: point.latitude, longitude: point.longitude))
prevPoint = point
return newCount
}
}
I like to use an extension for that
extension Array where Element: CLLocation {
var distance: Double {
guard count > 1 else { return 0 }
var previous = self[0]
return reduce(0) { (result, location) -> Double in
let distance = location.distance(from: previous)
previous = location
return result + distance
}
}
}
Usage:
locations.distance
I've created a map where you can press a start button. The application will then zoom in to your current location, and update the coordinate every 10 second and insert into an array of coordinates. Once I press the stop button, I've a polyline which draws lines between all coordinates. (Like the image below)
So my question is now:
How can I count the distance the polyline was drawn?
//Draw polyline on the map
let aPolyLine = MKPolyline(coordinates: self.locations, count: self.locations.count)
//Adding polyline to mapview
self.mapView.addOverlay(aPolyLine)
let startResult = self.locations.startIndex
let stopResult = self.locations.endIndex
//Retrieve distance and convert into kilometers
let distance = startResult.distance(to: stopResult)
let result = Double(distance) / 1000
let y = Double(round(10 * result)) / 10
self.KiloMeters.text = String(y) + " km"
My guess is that I cannot use startResult.distnace(to: stopResult) because, if I walk in a circle, the kilometer will show 0? right? I'm not sure, but it still dosent work. Nothing is showing when using the code like I've.
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
// MARK: - Variables
let locationManager = CLLocationManager()
// MARK: - IBOutlet
#IBOutlet weak var mapView: MKMapView!
// MARK: - IBAction
#IBAction func distanceTapped(_ sender: UIBarButtonItem) {
let locations: [CLLocationCoordinate2D] = [...]
var total: Double = 0.0
for i in 0..<locations.count - 1 {
let start = locations[i]
let end = locations[i + 1]
let distance = getDistance(from: start, to: end)
total += distance
}
print(total)
}
func getDistance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) -> CLLocationDistance {
// By Aviel Gross
// https://stackoverflow.com/questions/11077425/finding-distance-between-cllocationcoordinate2d-points
let from = CLLocation(latitude: from.latitude, longitude: from.longitude)
let to = CLLocation(latitude: to.latitude, longitude: to.longitude)
return from.distance(from: to)
}
}
I got this data from the socket and I need to change the rotation of
car base on changed location. how can I rotate(left, right, top,
bottom) car?
{
carType = Sedan;
latitude = "41.738751";
longitude = "-88.274285";
}
I also try this but doesn't work:
float dy = newLocation.latitude - oldLocation.latitude;
float dx = cosf(M_PI/180*oldLocation.latitude)*(newLocation.longitude - oldLocation.longitude);
float angle = atan2f(dy, dx);
driverMarker.rotation = angle;
You can change the rotation like:
marker.rotation = angle value
You can use this to get the angle for rotating the car marker:
func angleBetween(oldLocation: CLLocation, newLocation: CLLocation) -> Double {
let originLocation = CLLocation(latitude: newLocation.coordinate.latitude - oldLocation.coordinate.latitude, longitude: newLocation.coordinate.longitude - oldLocation.coordinate.longitude)
let bearingRadians = atan2(originLocation.coordinate.latitude, originLocation.coordinate.longitude)
let bearingDegrees = bearingRadians * 180 / Double.pi
return bearingDegrees
}
And you can use it like this
let angle = angleBetween(oldLocation: oldLocation, newLocation: newLocation)
And you can use marker.rotation like #Daljeet said:
marker.rotation = angle
Use Core Location delegate method.
It will give the direction of your phone.
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading)
{
let direction = newHeading.trueHeading as Double
print("Direction :- \(direction)");
currentLoaction.rotation = direction
}
Set those direction for your location marker.
here: currentLoaction is
var currentLoaction:GMSMarker = GMSMarker()
Hope this will help you if you used Google Map.
I need to find equidistant locations on a MKPolyline to add annotations as shown below.
My function to get locations on MKPolyline is given below, I have the values start and end coordinates of Polyline. But the locations are slightly ,moving out of polyline as shown in the image below
My function to find location is
func getEquidistantPoints(from startPoint: CLLocationCoordinate2D, to endPoint: CLLocationCoordinate2D, numberOfPoints: Int) -> [CLLocationCoordinate2D] {
var midPoints: [CLLocationCoordinate2D] = []
var newPoint: CLLocationCoordinate2D = CLLocationCoordinate2DMake(0, 0)
let count = numberOfPoints + 1
let latitudeModifier = (endPoint.latitude - startPoint.latitude) / Double(count)
let longitudeModifier = (endPoint.longitude - startPoint.longitude) / Double(count)
for i in 0..<count {
newPoint.latitude = CLLocationDegrees(startPoint.latitude + (latitudeModifier * Double(i)))
newPoint.longitude = CLLocationDegrees(startPoint.longitude + (longitudeModifier * Double(i)))
midPoints.append(newPoint)
}
return midPoints
}
In viewdidload
let coordinatesArray = getEquidistantPoints(from: sourceCoordinate, to: destinationCoordinate, numberOfPoints: 5)
for coordinate in coordinatesArray {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
self.mapView.addAnnotation(annotation)
}
How can I solve this error in calculating locations?
The problem is that the earth is round. So your "line" is not a line; it is a curve traced out on the surface of a nominal sphere. You cannot find "equidistant" points along the line using your simple-minded method. You need to use some much more serious math than you are using. This will prove to be far from trivial.
I'm working on an App where I get the user location and some custom pins (Restaurants) that I added. What I want to do is set radius (between 5km and 10km) distance between the user and the restaurants and then show those that are within the area.
My question is, how can I set that radius and get the locations within it
Best regards!
You can do some distance calculations to determine what pins to show.
let pins = Array<MKPointAnnotation>() // This is an empty array, so just use your array of locations or add to this.
if let currentLocation = locationManager.location?.coordinate {
for pin in pins {
let locationMapPoint = MKMapPointForCoordinate(currentLocation)
let pinMapPoint = MKMapPointForCoordinate(pin.coordinate)
let distance = MKMetersBetweenMapPoints(locationMapPoint, pinMapPoint)
if distance >= 5000 && distance <= 10000 {
self.map.addAnnotation(pin)
}
}
}
If you wanted something tidier, you could also do this.
let pins = Array<MKPointAnnotation>()
if let currentLocation = locationManager.location?.coordinate {
let filtered = pins.filter { $0.coordinate.distance(to: currentLocation) >= 5000 && $0.coordinate.distance(to: currentLocation) <= 10000 }
self.map.addAnnotations(filtered)
}
You'll need this extension aswell.
extension CLLocationCoordinate2D {
func distance(to coordinate: CLLocationCoordinate2D) -> Double {
return MKMetersBetweenMapPoints(MKMapPointForCoordinate(self), MKMapPointForCoordinate(coordinate))
}
}