Checking if point is in front - ios

I'm building a small application that takes the user's device's lat/long position as well as the heading (true north) and tells you what sort of points of interests are in front of the user. I am getting all my points of interests from google maps api.
I thought the easiest way to do this would be a dot product calculation between my forward vector and the AB vector however I have some false positives.
Here is my code:
func isFront(_ p1 : Point, _ p2 : Point, _ p1Heading : Double) -> Bool {
let forward = Point(cos(p1Heading), sin(p1Heading))
let AB = Point(p2.x - p1.x, p2.y - p1.y)
let lenAB = (AB.x * AB.x + AB.y * AB.y).squareRoot()
let normalAB = Point(AB.x / lenAB, AB.y / lenAB)
let dot = (normalAB.x * forward.x + normalAB.y * forward.y)
return (dot > 0)
}
So something that is coming back as true but I think shouldnt is:
My location(lat/lng): 42.359291, -71.059638 heading: 173.89306640625
Point of interest (lat/lng): 42.359980, -71.060303
Is this a good approach in figuring out if things are in front or should I look into doing something different?

Yes dot product is a good approach for this but I do not see any dot product in your equations/code I see only messed up gibberish there... If I get it right:
p1 is device position
forward is forward direction
p2 is tested point
then it should be:
let forward = Point(cos(p1Heading), sin(p1Heading))
let dot = ((p2.x-p1.x)*forward.x)+((p2.y-p1.y)*forward.y)
return (dot>0.0)
As you can see no lengths are required. Also no normal (even if your normal is not a normal but some gibberish). I think you should read some book/tutorial on vector math basic and check how things are computed here the 2D stuff you mess up:
dot(a,b) = a.x*b.x + a.y*b.y
normal(a) = Point(a.y,-a.x)

Related

Find distance of location to route in Google Maps SDK

I´m developing an iPhone app, and I need some help with this case:
I need to check, if user leave google maps route (GMSPolyline) and if distance from user location to nearest point of route is more than 40 meters -- I need to rebuild route.
I can't find the right algorithm to detect if distance from user to route is more than 40 meters.
I've tried to use this method to find projection of user location (converted to CGPoint by CGPointMake) on route :
+ (CGPoint)projectionOfPoint:(CGPoint)origPoint toSegmentP1:(CGPoint)p1 p2:(CGPoint)p2 {
// for case line is parallel to x axis
if (p2.y == p1.y) {
return CGPointMake(origPoint.x, p1.y);
// for case line is parallel to y axis
} else if (p2.x == p1.x) {
return CGPointMake(p1.x, origPoint.y);
}
// line from segment
CGFloat kKoefLine1 = (p2.x - p1.x)/(p2.y - p1.y);
CGFloat bKoefLine1 = p1.y - kKoefLine1*p1.x;
// perpendicular line
CGFloat kKoefLine2 = -1/kKoefLine1;
CGFloat bKoefLine2 = origPoint.y - kKoefLine2*origPoint.x;
// cross point
CGFloat krossX = (bKoefLine2 - bKoefLine1)/(kKoefLine1 - kKoefLine2);
CGFloat krossY = kKoefLine2*krossX + bKoefLine2;
return CGPointMake(krossX, krossY);}
Then I calculate distance from returned projection (converted to CLLocation) and user location, but it doesn't works.
P.S.: I will be thankful if solution would be written on swift.
There is a GMSGeometryIsLocationOnPath function in the GMSGeometryUtils module in the Google Maps SDK.
You should be able to use that to calculate what you need.
Pseudocode (not tested):
let currentLocation: CLLocationCoordinate2D = ...
let routePath: GMSPath = routePolyline.path
let geodesic = true
let tolerance: CLLocationDistance = 40
let within40Meters = GMSGeometryIsLocationOnPath(currentLocation, routePath, geodesic, tolerance)
for swift 5.0 and based on #Arthur answer I wrote follwoing function
func isInRoute(posLL: CLLocationCoordinate2D, path: GMSPath) -> Bool
{
let geodesic = true
let tolerance: CLLocationDistance = 40
let within40Meters = GMSGeometryIsLocationOnPathTolerance(posLL, path, geodesic, tolerance)
return within40Meters
}
While I don't recall much about the GMS SDK off the top of my head, before I give you an answer, I will say that nobody on here will write your code for you. That's your job and should be done on your time. You haven't given any background as to how far you've gotten in terms of calculating routes, whether or not you've figured out how to calculate distance at all, etc.
With that being said, routes on Google Maps are comprised of "legs", which denote a path to take before a turn is made in efforts to reach the end destination. By querying your "route" dictionary, you can extract an array of dictionaries where each element (which is a dictionary) contains metadata about a "leg". You can then loop through that array, go through each dictionary and extract the "distance" value, and sum them to a single "distance" var.
You can recalculate this as often as needed and use a conditional to check whether or not the leg distance sum is < 40M, else rebuild.
link to an article that should help (I didn't have the time to go through the entire thing for you, so do your due diligence and research) here.

How to generate a Random Floating point Number in range, Swift

I'm fairly new to Swift, only having used Python and Pascal before. I was wondering if anyone could help with generating a floating point number in range. I know that cannot be done straight up. So this is what I've created. However, it doesn't seem to work.
func location() {
// let DivisionConstant = UInt32(1000)
let randomIntHeight = arc4random_uniform(1000000) + 12340000
let randomIntWidth = arc4random_uniform(1000000) + 7500000
XRandomFloat = Float(randomIntHeight / UInt32(10000))
YRandomFloat = Float(randomIntWidth / UInt32(10000))
randomXFloat = CGFloat(XRandomFloat)
randomYFloat = CGFloat(YRandomFloat)
self.Item.center = CGPointMake(randomXFloat, randomYFloat)
}
By the looks of it, when I run it, it is not dividing by the value of the DivisionConstant, so I commented this and replaced it with a raw value. However, self.Item still appears off screen. Any advice would be greatly appreciated.
This division probably isn't what you intended:
XRandomFloat = Float(randomIntHeight / UInt32(10000))
This performs integer division (discarding any remainder) and then converts the result to Float. What you probably meant was:
XRandomFloat = Float(randomIntHeight) / Float(10000)
This is a floating point number with a granularity of approximately 1/10000.
Your initial code:
let randomIntHeight = arc4random_uniform(1000000) + 12340000
generates a random number between 12340000 and (12340000+1000000-1). Given your final scaling, that means a range of 1234 and 1333. This seems odd for your final goals. I assume you really meant just arc4random_uniform(12340000), but I may misunderstand your goal.
Given your comments, I think you've over-complicated this. The following should give you a random point on the screen, assuming you want an integral (i.e. non-fractional) point, which is almost always what you'd want:
let bounds = UIScreen.mainScreen().bounds
let x = arc4random_uniform(UInt32(bounds.width))
let y = arc4random_uniform(UInt32(bounds.height))
let randomPoint = CGPoint(x: CGFloat(x), y: CGFloat(y))
Your problem is that you're adding the the maximum value to your random value, so of course it's always going to be offscreen.
I'm not sure what numbers you're hoping to generate, but what you're getting are results like:
1317.0, 764.0
1237.0, 795.0
1320.0, 814.0
1275.0, 794.0
1314.0, 758.0
1300.0, 758.0
1260.0, 809.0
1279.0, 768.0
1315.0, 838.0
1284.0, 763.0
1273.0, 828.0
1263.0, 770.0
1252.0, 776.0
1255.0, 848.0
1277.0, 847.0
1236.0, 847.0
1320.0, 772.0
1268.0, 759.0
You're then using this as the center of a UI element. Unless it's very large, it's likely to be off-screen.

UIBezierPath Percent of Length at Point

I'm building an app that features some graphical manipulation. I'm storing shapes as UIBezierPaths, and I want to allow users to touch points along the line to create saved locations. Using the wonderful answer to this question, and more specifically, this project, I'm able to place a point on a line knowing the percentage of its length the point rests on. This is half of my problem.
I want a way to take a point on a path, and derive the percent of its length.
My math-fu is extremely weak. I've studied bezier curves but I simply don't have the math to understand it.
I would humbly submit that "go back and learn geometry and trigonometry" is a correct answer, but sadly one I don't have time for at present. What I need is a way to fill in this method:
- (CGFloat)percentOfLengthAtPoint:(CGPoint)point onPath:(UIBezierPath*)path
Any help appreciated!
I have working code that solves my problem. I'm not particularly proud of it; the overall technique is essentially a brute-force attack on a UIBezierPath, which is kind of funny if you think about it. (Please don't think about it).
As I mentioned, I have access to a method that allows me to get a point from a given percentage of a line. I have taken advantage of that power to find the closest percentage to the given point by running through 1000 percentage values. To wit:
Start with a CGPoint that represents where on the line the user touched.
let pointA = // the incoming CGPoint
Run through the 0-1 range in the thousands. This is the set of percentages we're going to brute-force and see if we have a match. For each, we run pointAtPercentOfLength, from the linked project above.
var pointArray:[[String:Any]] = []
for (var i:Int = 0; i <= 1000; i++) {
let value = CGFloat(round((CGFloat(i) / CGFloat(1000)) * 1000) / 1000)
let testPoint = path.pointAtPercentOfLength(value)
let pointB = CGPoint(x: floor(testPoint.x), y: floor(testPoint.y))
pointArray.append(["point" : pointB, "percent" : value])
}
That was the hard part. Now we take the returning values and calculate the distance between each point and the touched point. Closest one is our winner.
// sort the damned array by distance so we find the closest
var distanceArray:[[String:Any]] = []
for point in pointArray {
distanceArray.append([
"distance" : self.distanceFrom(point["point"] as! CGPoint, point2: pointA),
"point" : point["point"],
"percent" : point["percent"] as! CGFloat
])
}
Here's the sorting function if you're interested:
func distanceFrom(point1:CGPoint, point2:CGPoint) -> CGFloat {
let xDist = (point2.x - point1.x);
let yDist = (point2.y - point1.y);
return sqrt((xDist * xDist) + (yDist * yDist));
}
Finally, I sort the array by the distance of the values, and pick out the winner as our closest percent.
let ordered = distanceArray.sort { return CGFloat($0["distance"] as! CGFloat) < CGFloat($1["distance"] as! CGFloat) }
ordered is a little dictionary that includes percent, the correct value for a percentage of a line's length.
This is not pretty code, I know. I know. But it gets the job done and doesn't appear to be computationally expensive.
As a postscript, I should point to what appears to be a proper resource for doing this. During my research I read this beautiful article by David Rönnqvist, which included an equation for calculating the percentage distance along a path:
start⋅(1-t)3 + 3⋅c1⋅t(1-t)2 + 3⋅c2⋅t2(1-t) + end⋅t3
I was just about to try implementing that before my final solution occurred to me. Math, man. I can't even brain it. But if you're more ambitious than I, and wish to override my 30 lines of code with a five-line alternative, everyone would appreciate it!
I think your approach is sound, but you could do this far more efficiently.
Instead of creating an two arrays of dicts (with a thousand elements each) and then sorting the array - just use a while loop to move from 0.0 to 1.0, calculate the distance to the touch point and keep track of the minimum distance.
For example:
var t:CGFloat = 0.0
let step:CGFloat = 0.001
var minDistance:CGFloat = -1.0
var minPoint:CGPoint = CGPointZero
var minT:CGFloat = -1;
while (t<1.0) {
let point = pointAtPercentOfLength(t)
let distance:CGFloat = self.distanceFrom(point, point2: pointA)
if (minDistance == -1.0 || distance < minDistance) {
minDistance = distance
minPoint = point
minT = t
}
t += step
}
print("minDistance: \(minDistance) minPoint: \(minPoint.x) \(minPoint.y) t\(minT)\n")

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 determine the next POI in a navigation route?

I have a route (MKPolyline derived from an MKRoute retrieved from Apple's MKDirections API) and a bunch of points of interest (array of MKAnnotations) that are close to the route.
I would like to know how to select the next POI the user is going to meet following the route, in order to present it through a UI.
Two different approaches come to mind, but none of them is really adequate:
The first one would be to mark the POIs as checked each time you get close enough to them, and simply display the first unmarked POI in the array (we'll assume that they are correctly ordered). The problem is that if for a reason or another one of the POIs is not checked, then the app will forever display it instead of displaying the actual next POI coming. This situation can arise e.g. if the user followed a slightly different route than the one suggested, that didn't come close enough to the POI to get it checked; or the user starts the navigation after the first POI; etc.
The second one one would be to select the POI closest to the user (probably also with a marking system to avoid presenting the POI you just checked). But this would only work for routes straight enough: sometimes in mountain zones or other sinuous routes you can get closer to a point that you will actually cross later. I expect this situation to happen actually quite often.
Any idea?
When I had to implement a turn-by-turn in one of our apps I used what you describe as first bullet. To figure out if user diverged from original polyline I calculated distance between current position and line segments each time I read a new position. Once I detected I was NOT following the path, I re-calculated the route while showing a "Recalculating..." message for user.
This is my code
- (BOOL)isCoordinate:(CLLocationCoordinate2D)coordinate closeToPolyline:(MKPolyline *)polyline {
CLLocationCoordinate2D polylineCoordinates[polyline.pointCount];
[polyline getCoordinates:polylineCoordinates range:NSMakeRange(0, polyline.pointCount)];
for (int i = 0; i < polyline.pointCount - 1; i++) {
CLLocationCoordinate2D a = polylineCoordinates[i];
CLLocationCoordinate2D b = polylineCoordinates[i + 1];
double distance = [self distanceToPoint:MKMapPointForCoordinate(coordinate) fromLineSegmentBetween:MKMapPointForCoordinate(a) and:MKMapPointForCoordinate(b)];
if (distance < 25) {
return YES;
}
}
return NO;
}
- (double)distanceToPoint:(MKMapPoint)p fromLineSegmentBetween:(MKMapPoint)l1 and:(MKMapPoint)l2 {
double A = p.x - l1.x;
double B = p.y - l1.y;
double C = l2.x - l1.x;
double D = l2.y - l1.y;
double dot = A * C + B * D;
double len_sq = C * C + D * D;
double param = dot / len_sq;
double xx, yy;
if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
xx = l1.x;
yy = l1.y;
}
else if (param > 1) {
xx = l2.x;
yy = l2.y;
}
else {
xx = l1.x + param * C;
yy = l1.y + param * D;
}
return MKMetersBetweenMapPoints(p, MKMapPointMake(xx, yy));
}
Then I call - (BOOL)isCoordinate:(CLLocationCoordinate2D)coordinate closeToPolyline:(MKPolyline *)polyline { with coordinate being users current location and polyline being path from your MKDirections.
In my case I wouldn't allow more than 25 meters off but it might depend on your lat/lng precision.
Maybe it will help you or someone.
Use the following pseudometric, which I'll call route distance. Intuitively, it works like highway location markers. Assume that the route polyline does not touch or cross itself (is simple). For each point q on some segment pr of the polyline where p comes first, the location of that point in our 1D coordinate system is the Euclidean (spherical?) distance from p to q, plus the lengths of all segments that come before pr. The route distance between two such points is the absolute value of the difference of their locations in our 1D coordinate system. Extend route distance to points off of the route by treating such points as the closest point on the route (the answers to this question about computing point-to-segment distance should be helpful for computing the closest route point).
Present the closest POI to the user by route distance (check it off when this distance is sufficiently small).

Resources