Geo points in area - ios

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;
}

Related

Calculate coordinates of map square from map center

I want to get the coordinates of the corners of the rectangle. Or to find the coordinate of the north-westest most point, 50 km from the map centre.
Does anyone know how I can do that?
The point is when I move around the map, I want to always have a rectangle(the rectangle does not need to drew, I just need its coordinates for a backend request), with it's corners always at 50 km from the current centre of the map.
I'm thinking of using somehow the distance function from CLLocation, but in this case I have the distance, but not one of the coordinates.
50km = mapCenterLocation.distance(from: coordinatesUnknown)
Not really sure what do you mean, but maybe this can help
func getNewTargetCoordinate(position: CLLocationCoordinate2D, userBearing: Float, distance: Float)-> CLLocationCoordinate2D{
//haversine formula
//r is earth radius
let r = 6378140.0
let latitude1 = position.latitude * (Double.pi/180);
let longitude1 = position.longitude * (Double.pi/180);
//bearing for user heading in degree
let brng = Double(userBearing) * (Double.pi/180);
//calculating user new position based on user distance and bearing can be seen at haversine formula
var latitude2 = asin(sin(latitude1)*cos(Double(distance)/r) + cos(latitude1)*sin(Double(distance)/r)*cos(brng));
var longitude2 = longitude1 + atan2(sin(brng)*sin(Double(distance)/r)*cos(latitude1),cos(Double(distance)/r)-sin(latitude1)*sin(latitude2));
//converting latitude as degree
latitude2 = latitude2 * (180/Double.pi)
longitude2 = longitude2 * (180/Double.pi)
// return location of user
return CLLocationCoordinate2DMake(latitude2, longitude2)
}
This work for NE direction and distance in meters
for the north-west direction, I think you can just put 135 for the degree and 5000 for distance.
For the position, you need to put map center location.
edit:
For custom rectangle., you can first check for the diagonal degree
func getDiagonalDegree(x: Float, y:Float) -> Float{
return atan2(y,x)*(180/Double.pi)
}
So now you can get that returned diagonal degree to and put it in getNewTargetCoordinate. New bearing is 270+diagonalDegree.
Not sure if I understand you correctly, but I think this could help, or at least point you on some direction
CLLocationCoordinate2D northWest;
northWest = [mapView convertPoint:CGPointMake(0, 0) toCoordinateFromView:mapView];
With this you will get the coordinates for the top left corner of the map, I think you just need to adjust this to set a point 50 km of your center and get the coordinate with this same logic.

Multiple Locations in apple watch

I had to show locations around the user location. The locations info will arrive from the server. I am using 'for' loop to parse the location and add to map with annotation pin. As of now, it shows only the last arrived location. But the requirement is such that multiple locations should be shown with user location as Center point. As of now I am able to show only one pin. Please help how to achieve this?
for (NSDictionary* dictionary in responseDict)
{
NSString *latitudeString=[NSString stringWithFormat:#"%#",[dictionary valueForKey:#"LATITUDE"]];
double latitude=[latitudeString doubleValue];
NSString *longitudeString=[NSString stringWithFormat:#"%#",[dictionary valueForKey:#"LONGITUDE"]];
double longitude=[longitudeString doubleValue];
NSLog(#"the LATITUDE AND LONGITUDE is %f, %f",latitude,longitude);
CLLocationCoordinate2D locationCoordinate;
locationCoordinate.latitude=latitude;
locationCoordinate.longitude=longitude;
[self.mapView addAnnotation:locationCoordinate withPinColor:WKInterfaceMapPinColorPurple];
MKCoordinateSpan coordinateSpan = MKCoordinateSpanMake(0.05, 0.05);
[self.mapView setRegion:(MKCoordinateRegionMake(locationCoordinate, coordinateSpan))];
}
`
You zoom on a single location in each iteration of the loop, so eventually you end up zoomed on the last location. Sadly WKInterfaceMap doesn't have a showAnnotations method as MKMapView does, so you need to write a function yourself to achieve showing an MKCoordinateRegion including several annotations.
I haven't written any Obj-C for a while, but here's the function in Swift that shows two annotations on a Map and works on watchOS:
func showTwoMapCoordinates(first: CLLocationCoordinate2D, second: CLLocationCoordinate2D)->MKCoordinateRegion{
let center = CLLocationCoordinate2D(latitude: (first.latitude+second.latitude)/2, longitude: (first.longitude+second.longitude)/2)
var latDelta:CLLocationDegrees {
let delta = abs((first.latitude-second.latitude)*1.4)
if delta > 0 {
return delta
} else {
return 0.1
}
}
var lonDelta:CLLocationDegrees {
let delta = abs((first.longitude-second.longitude)*1.4)
if delta > 0 {
return delta
} else {
return 0.1
}
}
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
return MKCoordinateRegion(center: center, span: span)
}
To extend this to n points, where n>2, all you need to do is iterate through each pair of points you want to display, find the maximum deltas the same way as in the function above and set the center and span using the function above called on the two points that yielded the max deltas.

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
}
}

How to tell if Coordinate is in between two other coordinates

I have an array of CLLocations which represent the route user calculated.
User has an option to add certain POIs to that route and I need to know the way to tell where exactly on the route that POI will be.
What I came up with is next:
I would go through my route array, and grab a pair of CLLocations and calculate a distance from my POI to both of those coordinates and save that value.
I can do so for all pairs of coordinates on the route, and then see which distance is the smallest and that is how I would know if my coordinate is between two other ones.
Is there any other method of doing it but this one ?
This is how you can achieve this:
- (CLLocation*)closestLocationToLocation:(CLLocation*)currLocation {
CLLocationDistance minDistance;
CLLocation *closestLocation = nil;
for (CLLocation *location in arrayOfLocations) {
CLLocationDistance distance = [location distanceFromLocation:currLocation];
if (distance <= minDistance || closestLocation == nil) {
minDistance = distance;
closestLocation = location;
}
}
return closestLocation;
}

How to set Monotouch map between two points

How can I center a map between two points? Sort of like when the native map application generates directions between location A and location B. I'm got a start coordinate and an end coordinate and I'll like to show two pins. I can place the pins in place, but I'm not sure how to set the center of the map.
Do I need to find the math to work out the exact distance from the points and set the map to that location? Is there a built in function for this?
this.currentMapView.SetCenterCoordinate (annotation.Coordinate, true);
Calculating the midpoint between two coordinates needs a simple formula. For example, let's say you have two coordinates: (x1,y1) and (x2,y2).
Their midpoint coordinate is: ( (x1+x2)/2, (y1+y2)/2 ).
So for example, in map coordinates, let's say you have the following start/end points:
a. long: 40, lat: 39
b. long: 41, lat: 38
Their midpoint coordinate is: ( (40+41)/2, (39+38)/2 ) = (40.5, 38.5)
So you set the map view's center coordinate to the outcome of this formula.
I am not aware of a built-in function for calculating this.
Taken from: http://codisllc.com/blog/zoom-mkmapview-to-fit-annotations/
BasicMapAnnotation is inherit class from MKAnnotation
private void GetRegion(MKMapView mapView)
{
var userWasVisible = mapView.ShowsUserLocation;
mapView.ShowsUserLocation = false; // ignoring the blue blip
// start with the widest possible viewport
var tl = new CLLocationCoordinate2D(-90, 180); // top left
var br = new CLLocationCoordinate2D(90, -180); // bottom right
foreach (var an in mapView.Annotations)
{
// narrow the viewport bit-by-bit
CLLocationCoordinate2D coordinate = ((BasicMapAnnotation) an).Coordinate;
tl.Longitude = Math.Min(tl.Longitude, coordinate.Longitude);
tl.Latitude = Math.Max(tl.Latitude, coordinate.Latitude);
br.Longitude = Math.Max(br.Longitude, coordinate.Longitude);
br.Latitude = Math.Min(br.Latitude, coordinate.Latitude);
}
var center = new CLLocationCoordinate2D
{
// divide the range by two to get the center
Latitude = tl.Latitude - (tl.Latitude - br.Latitude)*0.5
,
Longitude = tl.Longitude + (br.Longitude - tl.Longitude)*0.5
};
var span = new MKCoordinateSpan
{
// calculate the span, with 20% margin so pins aren’t on the edge
LatitudeDelta = Math.Abs(tl.Latitude - br.Latitude)*1.2
,
LongitudeDelta = Math.Abs(br.Longitude - tl.Longitude)*1.2
};
var region = new MKCoordinateRegion {Center = center, Span = span};
region = mapView.RegionThatFits(region); // adjusts zoom level too
mapView.SetRegion(region, true); // animated transition
mapView.ShowsUserLocation =
userWasVisible;
}
} ``

Resources