Point compass to one direction - ios

I have a UIImageView representing a simple knob: it has nothing to do with stereo hi-fi, it represents a sort of compass:
My goal is the following: I want the compass to point a precise direction, starting from the current position of the device (and it must update too: if the user turns the device elsewhere, the knob should rotate accordingly).
What I tried until now:
I have this function:
func getRadiansBearing()->Double{
// body of degreesToRadians: return degrees * M_PI / 180.0
// data[1]._lat and _lon contains my current position
let lat1 = degreesToRadians(data[1]._lat)
let lon1 = degreesToRadians(data[1]._lon)
// data[0]._lat and _lon contains the target position
let lat2 = degreesToRadians(data[0]._lat)
let lon2 = degreesToRadians(data[0]._lon)
let dLon = lon2 - lon1;
let y = sin(dLon)*cos(lat2)
let x = cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(dLon)
var radiansBearing = atan2(y, x);
if(radiansBearing < 0.0)
{
radiansBearing = radiansBearing+2*M_PI
}
return radiansBearing
}
and I call it this way, using the CoreLocation framework:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
guard let location = locations.last else {
return;
}
// data is an array of structs
data[1]._lat = location.coordinate.latitude
data[1]._lon = location.coordinate.longitude
geoAngle = getRadiansBearing()
}
and:
func locationManager(manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
let direction = -newHeading.trueHeading
let dir2 = degreesToRadians(direction)
UIView.animateWithDuration(2.0, animations: {
self.imgView.transform = CGAffineTransformMakeRotation((CGFloat(dir2) * CGFloat(M_PI) / 180) + CGFloat(self.geoAngle))
})
}
Problem is, for some reason the knob turns once into one (wrong) direction, then stops working.
Can you help me? Any tip is appreciated! :-)
P.S.: I red many related posts, none solves this issue

Consider latitude as x, and longitude as y for now.
Pre-requisites:
Destination location must be known.
Current location must be known.
I would show you how to calculate the angle towards the direction now. It is as follows!
Get the current location and destination location's latitude and longitude.
Pseudo Code:
Let, currentLocation = ( lat, long )
Let, destinationLocation = ( lat, long )
angleTowardsDestination = ?
Calculate the base and hypotenuse for Pythagorus theorem.
Now,
base = √((currentLocation.lat - destinationLocation.lat)2 - (currentLocation.long - currentLocation.long)2)
hypotenuse = √((currentLocation.lat - destinationLocation.lat)2 - (currentLocation.long - destinationLocation.long)2)
Calculate the angle now!
angleTowardsDestination = Cos-1( base / hypotenuse )
And, that's the angle you need in degrees. Feel free to convert it to radian if needed.
Kind Regards,
Suman Adhikari

Related

how to detect difference between 2 points on the map and consider the zoom?

I am using a map with some annotations, I want to group the nearest annotations in annotation group to not overlap in the design in case of user zoom out, but my problem is to how to know the distance between annotations and this distance should change when zooming in and out.
the distance should be between points with x and y formate note meters
So my question is to how to catch the difference between 2 points on the map and consider the zoom
// convert location to cLLocation
let cLLocation1 = CLLocation(latitude: post1Location?.lat ?? 0, longitude: post1Location?.lng ?? 0)
let cLLocation2 = CLLocation(latitude: post2Location?.lat ?? 0, longitude: post2Location?.lng ?? 0)
// this is return the dinsactence in metres but i don't need that
let distance = cLLocation1.distance(from: cLLocation2)
let annotaionPoint1 = MKMapPoint(cLLocation1.coordinate)
let annotaionPoint2 = MKMapPoint(cLLocation2.coordinate)
let xDistance = max(annotaionPoint1.x, annotaionPoint2.x) - min(annotaionPoint1.x, annotaionPoint2.x)
let yDistance = max(annotaionPoint1.y, annotaionPoint2.y) - min(annotaionPoint1.y, annotaionPoint2.y)
this is working but zoom in and zoom out no effect so I need zoom to make change
if min(xDistance, yDistance) <= 32 {
/// action
}

How to calculate CLLocation distance between 2 CLLocation points using as a path an array of CLLocations and not a straight line

I have 2 CLLocation coordinates and I wanna know the distance between them using as a path an array of coordinates that go from point A to B. I know how to calculate the distance from point A to B but the problem is that it seems to be measured in a straight line rather than following a given route. Any idea on how to do this efficiently?
It's very simple if you have all CLLocations. Just one line code can do it. For example:
var locations : [CLLocation] = [CLLocation.init(latitude: CLLocationDegrees(10.00000), longitude: CLLocationDegrees(100.00000)),
CLLocation.init(latitude: CLLocationDegrees(10.00001), longitude: CLLocationDegrees(100.00001)),
CLLocation.init(latitude: CLLocationDegrees(10.00002), longitude: CLLocationDegrees(100.00002)),
CLLocation.init(latitude: CLLocationDegrees(10.00003), longitude: CLLocationDegrees(100.00003)),
]
let totalDistance = locations.dropFirst().reduce((locations.first!, 0.0)) { ($1 , $0.1 + $0.0.distance(from: $1)) }.1
print(totalDistance)
I made E.Coms answer more readable:
guard let firstLocation = locations.first else { return }
let distance = locations.reduce((location: firstLocation, distance: 0.0)) { partialResult, nextLocation in
return (nextLocation, partialResult.distance + partialResult.location.distance(from: nextLocation))
}.distance

Adding CLLocationDistances

I'm trying to add CLLocationDistances and getting strange results. Here is my code:
var locations = [CLLocation]()
var totalDistance = CLLocationDistance()
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.locations.append(locations.first!)
if self.locations.count > 1 {
self.calculateTotalDistance()
}
}
func calculateTotalDistance() -> CLLocationDistance {
var y = 0
self.totalDistance = 0
while y < (locations.count - 1) {
let firstLocation = self.locations[y]
let secondLocation = self.locations[y + 1]
let distance = firstLocation.distanceFromLocation(secondLocation)
print(distance)
totalDistance += distance
y++
}
self.updateLabelWithText()
return totalDistance
}
private func updateLabelWithText() {
let distance = String(self.totalDistance)
let message = "Distance in meters: " + distance
self.distanceTextLabel.text = message
}
Essentially, every time I get a new CLLocation object from the system, I append it to my array. Then I iterate through the array, and get the distance between each individual points, and then add them together. I am testing the code on an actual device. As of now, when I run this code, even when I just sit down, the totalDistance variable reaches a count of 100 or so in 10 seconds, despite me not having moved anywhere close to 100 meters.
Also, in the calculateTotalDistance function, I print the distance calculated, and the distances don't seem right at all. Here is an example of what was printed to the console after the app launched for a few seconds:
0.0
1.15645918113224
1.06166528806247
1.06006756503664
1.05847219105153
16.1407724137949
9.67662215264722
0.0
1.15645918113224
1.06166528806247
1.06006756503664
1.05847219105153
16.1407724137949
9.6766221526472
Again, these are values just from when I'm sitting down, so I'm obviously not moving 16 meters, or 9 meters at a time.
Any idea what i'm doing wrong here?
thanks
This is because of accuracy of GPS. Check horizontalAccuracy of CLLocation before adding it to array.
You should also set desiredAccuracy of CLLocationManger to kCLLocationAccuracyBest.

How to check if CLCircularRegions intersect

I'm developing an iOS app (with Swift) that keeps a log of a user's location history. As part of a search algorithm, I'd like to check if two CLCircularRegions intersect, but I can't seem to find a Core Location method or function to do it. CLCircularRegion has the containsCoordinate method, but that's not exactly what I need. I also know that Map Kit includes functions to check for intersecting MKMapRects, but since I'm not actually working with maps, those solutions don't seem ideal.
I hope I'm missing something obvious, but I can't seem to figure it out. How can I check if two CLCircularRegions intersect?
If you don't mind small inaccuracies, you can assume that the regions are small enough that the curvature of the Earth is negligible therefore the regions can be treated as planes.
In this case, just check whether the distance of the two center points is smaller than the sum of the radii. Two circles intersect if and only if their centers are closer than the sum of their radii.
CLCircularRegion r1, r2;
const double meanEarthRad = 6371009;
const double metersPerDegree = 2 * M_PI * meanEarthRad / 360;
double dLat = r2.center.latitude - r1.center.latitude;
double dLon = r2.center.longitude - r1.center.longitude;
double actCenterDist = hypot(dLat, dLon) * metersPerDegree;
double minCenterDist = r1.radius + r2.radius;
if (actCenterDist < minCenterDist) {
// the regions intersect
}
Swift 4.2 version of The Paramagnetic Croissant's answer
extension CLCircularRegion {
func intersects(_ r2: CLCircularRegion) -> Bool {
let r1 = self
let meanEarthRad: Double = 6371009
let metersPerDegree = 2 * Double.pi * meanEarthRad / 360
let dLat = r2.center.latitude - r1.center.latitude
let dLon = r2.center.longitude - r1.center.longitude
let actCenterDist = hypot(dLat, dLon) * metersPerDegree
let minCenterDist = r1.radius + r2.radius
return actCenterDist < minCenterDist
}
}

Geo points in area

I am working on iOS app where at some point I want to get user's location and present her all point of interests on map that are inside a circular area where centre of this area is user's current location and radius is constant. Points of interests are stored in database with their coordinates (latitude, longitude).
I have already managed to get user's location. Now I am trying to figure out how to calculate if certain coordinates are in that area.
I was thinking that I can calculate distance of some point from centre using this equation:
d = sqrt((centre_latitude - point_latitude)^2 + (centre_longitude - point_longitude)^2)
Where d is distance of that point from circle centre. Then I could simply compare d with radius.
I am not sure if this is right and also efficient approach. I can imagine that if I have thousands of points this would be really slow (query database for each point then do the math).
You can try this:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
float lon= // longitude you want to compare to your current postion
float lat=//your latitude you want to compare to your current position
float rad=//radius , if you give this 100m, then it checks the given points are within the 100m from you are not
CLLocation *centerLocation = [[CLLocation alloc] initWithLatitude:lat
longitude:lon];
CLLocation *lastLocation=[locations lastObject];
//display current lat and lon in text fields
currentLat.text=[NSString stringWithFormat:#"%f",lastLocation.coordinate.latitude];
currentLon.text=[NSString stringWithFormat:#"%f",lastLocation.coordinate.longitude];
CLLocationDistance distance = [lastLocation distanceFromLocation:centerLocation];
if (distance<=rad) {
// you are within the radius
}
CLLocationAccuracy accuracy = [lastLocation horizontalAccuracy];
if(accuracy <=10) { //accuracy in metres
[manager stopUpdatingLocation];
}
}
You can use Haversine formula.
I have it implemented in Java for Android, maybe it helps you.
private static double calculateDistanceInMiles(Location StartP, Location EndP) {
//Haversine formula
//double Radius=6371; // to get distance in kms
double Radius=3963.1676; //to get distance in miles
double lat1 = StartP.getLatitude();
double lat2 = EndP.getLatitude();
double lon1 = StartP.getLongitude();
double lon2 = EndP.getLongitude();
double dLat = Math.toRadians(lat2-lat1);
double dLon = Math.toRadians(lon2-lon1);
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
double c = 2 * Math.asin(Math.sqrt(a));
return Radius * c;
}

Resources