Display lateral view same like google map in my application - ios

I am developing one navigation based application in which i used google map and google direction api for route navigation but i stuck at one point, i need to show same google map lateral view which is open when we start the navigation.
I have R&D lots off on it but not able to do exactly same like google map lateral view.
I try with following
GMSCameraPosition *camera =[GMSCameraPosition cameraWithTarget:marker.position zoom:50.0 bearing:120 viewingAngle:90];
[[self getMapView] animateToCameraPosition:camera];
not succeed to display same.
I need to show same google map as following when i start navigation.Can anybody help me in this point.Thanks in advance

You can achieve using proper bearing between 2 location. (present and target location)
func degreesToRadians(degrees: Double) -> Double { return degrees * .pi / 180.0 }
func radiansToDegrees(radians: Double) -> Double { return radians * 180.0 / .pi }
func getBearingBetweenTwoPoints1(point1 : CLLocation, point2 : CLLocation) -> Double {
let lat1 = degreesToRadians(degrees: point1.coordinate.latitude)
let lon1 = degreesToRadians(degrees: point1.coordinate.longitude)
let lat2 = degreesToRadians(degrees: point2.coordinate.latitude)
let lon2 = degreesToRadians(degrees: point2.coordinate.longitude)
let dLon = lon2 - lon1
let y = sin(dLon) * cos(lat2)
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
let radiansBearing = atan2(y, x)
return radiansToDegrees(radians: radiansBearing)
}
let bearingPoint = self.getBearingBetweenTwoPoints1(point1: location!, point2: self.featureLocation)
let camera = GMSCameraPosition.camera(withLatitude: (location?.coordinate.latitude)!, longitude: (location?.coordinate.longitude)!, zoom: 50, bearing: bearingPoint, viewingAngle: 45)
self.mapView?.animate(to: camera)

Related

Swift attach UIView to PolyLine and follow angle

I'm attempting to have a UIView follow a Polyline using the angle of the Polyline. The problem I'm facing is the rotation is never right unless the angle is lower than 50 degrees, which I'm thinking is just a coincidence. How can I get the rotation to follow the line?
Here's the code I'm using to detect the rotation.
func heading(from: MSGeopoint, to: MSGeopoint) -> Double {
let a = CLLocationCoordinate2D(latitude: from.position.latitude, longitude: from.position.longitude)
let b = CLLocationCoordinate2D(latitude: to.position.latitude, longitude: to.position.longitude)
let lat1 = a.latitude.degreesToRadians
let lon1 = a.longitude.degreesToRadians
let lat2 = b.latitude.degreesToRadians
let lon2 = b.longitude.degreesToRadians
let dLon = lon2 - lon1
let y = sin(dLon) * cos(lat2)
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
let headingDegrees = atan2(y, x).radiansToDegrees
if headingDegrees >= 0 {
print("\(headingDegrees)")
return headingDegrees
} else {
print("\(headingDegrees + 360)")
return headingDegrees + 360
}
}
The MSGeoPoint is primarily a wrapper for CoreLocation using Bing maps but here's how you instantiate the from and to.
let fromGeoPoint = MSGeopoint(latitude: from.latitude, longitude: from.longitude)
let toGeoPoint = MSGeopoint(latitude: to.latitude, longitude: to.longitude)
And the degreesToRadians.
extension BinaryInteger {
var degreesToRadians: CGFloat { return CGFloat(Int(self)) * .pi / 180 }
}
Here's also how I'm creating the view.
func createDistanceView() -> UIView {
let distanceView: DistanceView = UIView.fromNib()
distanceView.layer.borderColor = UIColor(named: "Blue")?.cgColor ?? UIColor.blue.cgColor
distanceView.layer.borderWidth = 3
distanceView.layer.masksToBounds = true
let heading = heading(from: from, to: to).rounded(.toNearestOrAwayFromZero)
distanceView.distanceLabel.text = "Your almost there"
return distanceView
}

Converting two coordinate points to distance in west-east-facing and north-south-facing direction

I'm trying to find out how to calculate the "west-east-facing" distance and the "north-south-facing" distance between two points respectively given the coordinates of the two points (with latitude and longitude), in order to find out the degree in which the line differs from the northern direction.
In short I need to calculate x and y in meters in order to get the degree of alpha, when x and y are given by longitude and latitude.
I know CLLocation has a function to calculate the distance between two points:
distance(from location: CLLocation) -> CLLocationDistance
and I tried to open the source code of that function in order to figure out how to separate the two components but I did not find out how to open that code.
You can use this
func getDistanceAndAngle(positionA: CLLocation, positionB: CLLocation) -> (Float, Float, Float){
let distanceInMeters = Float(positionA.distance(from: positionB)) // result is in meters
print(distanceInMeters)
//search for the degree
let angle = bearingFromLocation(fromLocation: positionA.coordinate, toLocation: positionB.coordinate)
print("ANGLE", angle)
let xDistance = abs(distanceInMeters * cos(DegreesToRadians(degrees: angle)))
let yDistance = abs(distanceInMeters * sin(DegreesToRadians(degrees: angle)))
return (xDistance,yDistance,angle)
}
func bearingFromLocation(fromLocation:CLLocationCoordinate2D, toLocation: CLLocationCoordinate2D)-> Float{
let lat1 = DegreesToRadians(degrees: Float(fromLocation.latitude))
let lon1 = DegreesToRadians(degrees: Float(fromLocation.longitude))
let lat2 = DegreesToRadians(degrees: Float(toLocation.latitude))
let lon2 = DegreesToRadians(degrees: Float(toLocation.longitude))
let dLon = lon2 - lon1
let y = sin(dLon) * cos(lat2)
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
let radiansBearing = atan2(y, x)
print("radian", radiansBearing)
let degreesBearing = RadiansToDegrees(radians: radiansBearing)
print("deg", degreesBearing)
if (degreesBearing >= 0) {
return degreesBearing
} else {
return degreesBearing + 360.0
}
}
func DegreesToRadians(degrees: Float)->Float {return degrees * Float.pi / 180.0}
func RadiansToDegrees(radians: Float)->Float {return radians * 180.0/Float.pi}
notes:
the angle is from north to east
so north is 0 degree and east is 90 degree.
the X and Y is always positive. So if you want to make it negative to the left and down, you can try to put use degree to make it right.
You can convert point1 and point2 to MKMapPoint. It's gives you coordinates which is projected map in 2D plane. Then you can eaisily get difference of x's and y's.. And using simple math, you can calculate the alpha value. You can initialize MKMapPoint with CLLocationCoordinate2D.
https://developer.apple.com/documentation/mapkit/mkmappoint
Assuming we have:
let locationA = CLLocation(..)
let locationB = CLLocation(..)
I would probably still want to use Apple's provided functions and create new locations that only differ in latitude or longitude. Something like:
let latitudeA = CLLocation(latitude: locationA.latitude, longitude: locationA.longitude)
// notice I use latitude from B but KEEP longitude from A to keep them parallel)
let latitudeB = CLLocation(latitude: locationB.latitude, longitude: locationA.longitude)
let northSouthDistance = latitudeA.distance(from: latitudeB)
etc

MKCoordinatespan latitudedelta and longitudedelta calculation based on center coordinates of source and destination

I'm trying to implement map navigation from source to destination in app. I just want to dynamically calculate latitudedelta and longitudedelta based on center coords.
let midLat = (self.source.coordinate.latitude + self.destination.coordinate.latitude)/2
let midLang = (self.source.coordinate.longitude + self.destination.coordinate.longitude)/2
let centerCoordinate = CLLocationCoordinate2DMake(midLat, midLang)
self.mapView.setRegion(MKCoordinateRegionMake(centerCoOrdinate, MKCoordinateSpanMake(0.09, 0.09)), animated: true)
First of all you have a problem calculating the mid location, you must check this answer Determining Midpoint Between 2 Cooridinates using the extension proposed there
import Foundation
import MapKit
extension CLLocationCoordinate2D {
// MARK: CLLocationCoordinate2D+MidPoint
func middleLocationWith(location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
let lon1 = longitude * M_PI / 180
let lon2 = location.longitude * M_PI / 180
let lat1 = latitude * M_PI / 180
let lat2 = location.latitude * M_PI / 180
let dLon = lon2 - lon1
let x = cos(lat2) * cos(dLon)
let y = cos(lat2) * sin(dLon)
let lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) )
let lon3 = lon1 + atan2(y, cos(lat1) + x)
let center:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat3 * 180 / M_PI, lon3 * 180 / M_PI)
return center
}
}
then you use it like this
let middleLocation = self.source.coordinate.middleLocationWith(location: self.destination.coordinate)
self.mapView.setRegion(MKCoordinateRegionMake(middleLocation, MKCoordinateSpanMake(0.09, 0.09)), animated: true)
I hope this helps you

Calculate new coordinates with starting position and distance

I am trying to get the coordinates of a point, that is on a set distance from a starting position, but the end result is wrong.
First, I calculate the angle between the starting position and the desired destination:
private func calculateAngleBetweenLocations(currentLocation: CLLocationCoordinate2D, targetLocation: CLLocationCoordinate2D) -> Double {
let fLat = self.degreesToRadians(currentLocation.latitude);
let fLng = self.degreesToRadians(currentLocation.longitude);
let tLat = self.degreesToRadians(targetLocation.latitude);
let tLng = self.degreesToRadians(targetLocation.longitude);
let deltaLng = tLng - fLng
let y = sin(deltaLng) * cos(tLat)
let x = cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(deltaLng)
let bearing = atan2(y, x)
return self.radiansToDegrees(bearing)
}
Then, I calculate the new point, given a distance:
private func coordinatesForMovement(endLocation: CLLocationCoordinate2D, distance: Double) -> CLLocationCoordinate2D {
let angle = self.calculateAngleBetweenLocations(self.currentLocation, targetLocation: endLocation)
let x = self.currentLocation.latitude + distance * cos(angle)
let y = self.currentLocation.longitude + distance * sin(angle)
return CLLocationCoordinate2D(latitude: x, longitude: y)
}
And this is the result (the feet are the starting position, the blue marker is the destination and the red marker is where the new calculated point is). I've tried passing the distance in meters and kilometers and every other floating point position, but never got the correct result. Any ideas?
Ok, after some more digging, I found this answer, which resolves my problem. Here is my complete solution in swift:
internal func moveToLocation(location: CLLocationCoordinate2D, distance: Double) {
let angle = self.calculateAngleBetweenLocations(self.currentLocation, targetLocation: location)
let newLocation = self.coordinates(self.currentLocation, atDistance: distance, atAngle: angle)
self.moveUser(newLocation: newLocation)
}
private func coordinates(startingCoordinates: CLLocationCoordinate2D, atDistance: Double, atAngle: Double) -> CLLocationCoordinate2D {
let distanceRadians = atDistance / 6371
let bearingRadians = self.degreesToRadians(atAngle)
let fromLatRadians = self.degreesToRadians(startingCoordinates.latitude)
let fromLonRadians = self.degreesToRadians(startingCoordinates.longitude)
let toLatRadians = asin(sin(fromLatRadians) * cos(distanceRadians) + cos(fromLatRadians) * sin(distanceRadians) * cos(bearingRadians))
var toLonRadians = fromLonRadians + atan2(sin(bearingRadians) * sin(distanceRadians) * cos(fromLatRadians), cos(distanceRadians) - sin(fromLatRadians) * sin(toLatRadians));
toLonRadians = fmod((toLonRadians + 3 * M_PI), (2 * M_PI)) - M_PI
let lat = self.radiansToDegrees(toLatRadians)
let lon = self.radiansToDegrees(toLonRadians)
return CLLocationCoordinate2D(latitude: lat, longitude: lon)
}
private func calculateAngleBetweenLocations(currentLocation: CLLocationCoordinate2D, targetLocation: CLLocationCoordinate2D) -> Double {
let fLat = self.degreesToRadians(currentLocation.latitude);
let fLng = self.degreesToRadians(currentLocation.longitude);
let tLat = self.degreesToRadians(targetLocation.latitude);
let tLng = self.degreesToRadians(targetLocation.longitude);
let deltaLng = tLng - fLng
let y = sin(deltaLng) * cos(tLat)
let x = cos(fLat) * sin(tLat) - sin(fLat) * cos(tLat) * cos(deltaLng)
let bearing = atan2(y, x)
return self.radiansToDegrees(bearing)
}
private func degreesToRadians(x: Double) -> Double {
return M_PI * x / 180.0
}
private func radiansToDegrees(x: Double) -> Double {
return x * 180.0 / M_PI
}

Calculating bearing between two CLLocation points in Swift [duplicate]

This question already has answers here:
CLLocation Category for Calculating Bearing w/ Haversine function
(8 answers)
Closed 8 years ago.
I'm trying to calculate a bearing between two CLLocation points in swift-only code. I've run into some difficulty and was assuming this is a pretty simple function. Stack overflow didn't seem to have anything listed.
func d2r(degrees : Double) -> Double {
return degrees * M_PI / 180.0
}
func RadiansToDegrees(radians : Double) -> Double {
return radians * 180.0 / M_PI
}
func getBearing(fromLoc : CLLocation, toLoc : CLLocation) {
let fLat = d2r(fromLoc.coordinate.latitude)
let fLng = d2r(fromLoc.coordinate.longitude)
let tLat = d2r(toLoc.coordinate.latitude)
let tLng = d2r(toLoc.coordinate.longitude)
var a = CGFloat(sin(fLng-tLng)*cos(tLat));
var b = CGFloat(cos(fLat)*sin(tLat)-sin(fLat)*cos(tLat)*cos(fLng-tLng))
return atan2(a,b)
}
I'm getting an error with my atan2 call about lvalue cgfloat or something...
Here is an Objective-C solution
CLLocation Category for Calculating Bearing w/ Haversine function
which can easily be translated to Swift:
func degreesToRadians(degrees: Double) -> Double { return degrees * .pi / 180.0 }
func radiansToDegrees(radians: Double) -> Double { return radians * 180.0 / .pi }
func getBearingBetweenTwoPoints1(point1 : CLLocation, point2 : CLLocation) -> Double {
let lat1 = degreesToRadians(degrees: point1.coordinate.latitude)
let lon1 = degreesToRadians(degrees: point1.coordinate.longitude)
let lat2 = degreesToRadians(degrees: point2.coordinate.latitude)
let lon2 = degreesToRadians(degrees: point2.coordinate.longitude)
let dLon = lon2 - lon1
let y = sin(dLon) * cos(lat2)
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
let radiansBearing = atan2(y, x)
return radiansToDegrees(radians: radiansBearing)
}
The result type is Double because that is how all location coordinates are
stored (CLLocationDegrees is a type alias for Double).
This isn't exactly accurate, but you're probably looking for something along the lines of:
func XXRadiansToDegrees(radians: Double) -> Double {
return radians * 180.0 / M_PI
}
func getBearingBetweenTwoPoints(point1 : CLLocation, point2 : CLLocation) -> Double {
// Returns a float with the angle between the two points
let x = point1.coordinate.longitude - point2.coordinate.longitude
let y = point1.coordinate.latitude - point2.coordinate.latitude
return fmod(XXRadiansToDegrees(atan2(y, x)), 360.0) + 90.0
}
I appropriated the code from this NSHipster article that goes into more detail about what's wrong with it. The basic issue is that it's using the coordinates as though the world is flat (which it isn't, right?). Mattt's article can show you how to get the real directions using MKMapPoints instead of CLLocations.

Resources