SQLite query with location fields inside of region - ios

I have a database with two columns, latitude and longitude.
I wonder if it's possible in the Transact-SQL command to retrieve only the records in a defined region of the location passed as a parameter;
For example let's say you have the following records, which is all from the same location, but we're using them for testing purposes.
Now I have the SQL query:
[self.fmQueue inDatabase:^(FMDatabase *db)
{
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(latitude, longitude);
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:400.0f identifier:#"identifier"];
NSString* queryCommand = [NSString stringWithFormat:#"SELECT * FROM %# WHERE ?EXPRESSION_ABOUT_REGION?", readingsTable];
FMResultSet* result = [db executeQuery:queryCommand];
if (completionblock)
{
completionblock([self readingsArrayFromResultSet:result]);
}
}];
I could just retrieve records and then compare for every each one with a constructed CLLocationCoordinate2D, [region containsCoordinate:CLLocationCoordinate2D], but that feels so bad performant.
I'm looking for the most performant and appropriate solution to retrieve the desired region related location records in the T-SQL query.

Issues with the Previous Approximation
The previous answer here models the Earth as a Cartesian plane, with latitude and longitude as x and y, and then draws either a circle or a rectangle around the points---the actual shape does not matter, both methods have the same properties.
This can be fine if your constraints are really loose, and they may be here in your particular instance. For the sake of making this answer more useful to everybody I've answered the problem assuming your constraints are not quite as loose. For mobile geolocation apps in general this approximation causes problems. Maybe you've seen an app that's got it wrong. The resulting distances don't make any sense. There are several flaws with the previous approach:
Bad approximation: The scale of the Earth is big so simple approximations can be off by a scale of miles. This is bad if the user is in a car and really bad if the user is on foot. Simple tricks with degrees are not an improvement over circles or squares: latitude and longitude are not evenly distributed, and you need a full approximation like Haversine to get accurate data.
Excludes good points: Unless you severely expand the shape, your sample will exclude valid points.
Includes bad points: And the more you expand the shape to capture excluded valid points, the more you capture invalid points.
Broken sort order: Without the real calculation, you won't be able to correctly sort the output by distance. This means any ordered list you produce will be way off. Usually you want the points in order of closeness.
Two calculations: If you go back and fix it, you're doing two computations.
Single-point Haversine in C/Objective-C
The selection code is already in the other answer. This is the recomputation you referenced that you'll have to perform for each point after in Objective-C or C:
/////////////////////////////////////
//Fill these in or turn them into function arguments
float lat1=center_point.latitude;
float lon1=center_point.longitude;
float lat2=argument_point.latitude;
float lon2=argument_point.longitude;
/////////////////////////////////////
//Conversion factor, degrees to radians
float PI=3.14159265358979;
float f=PI/180;
lat1*=f; lon1*=f; lat2*=f; lon2*=f;
//Haversine Formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159):
float dlon = lon2 - lon1;
float dlat = lat2 - lat1;
float a = pow((sin(dlat/2)),2) + cos(lat1) * cos(lat2) * pow((sin(dlon/2)),2);
float c = 2 * asin(MIN(1,sqrt(a)));
float R = 6378.137;//km Radius of Earth
float d = R * c;
d *= 0.621371192; //optional: km to miles [statute] conversion factor
//NSString conversion?
//if (d >= .23) return [NSString stringWithFormat:#"%0.1f m",d]; //.23m ~ 400 yds
//d *= 5280; //miles to feet conversion factor
//d /= 3; //feet to yards
//int y=(int)d;
//return [NSString stringWithFormat:#"%d yds",y];
return d;
That should be all the tools you need to complete your task as discussed.
Haversine in SQLite
I would like to show you a direct SQLite-only solution, but I was never able to get Haversine to run satisfactorily directly inside of SQLite. You don't get a square root in SQLite. You don't get a pow() function, though you can repeat an argument times itself. You don't get sin, cos, or sinh. There are extensions that add some of these features. I don't know how well-supported they are compared to the base SQLite. Even with them it's going to be too slow.
People seem to recommend updating the columns with pre-computed sines. That's fine as long as you don't need to take the sine of a difference over a whole column, in which case you're writing a new column to the table every time you need to make a new calculation, which is terrible. At any rate, I'd like to show you a comparison of how slow SQLite is on computing the Haversine, but I can't get it to compute it at all. (I think my memory of SQLite being slow on this is actually a memory of MySQL on the server being slow.)
All-points Solution in Kerf
The preceding discussion I hope is a close-to-exhaustive look at what you can do with the standard tools.
The good news is if you do it right this calculation is fast on the phone. I built a thing for you that I think solves your problem in a better way. If you are willing to use another tool, in Kerf this problem is easy. I went back and committed to the repo vectorized operations for trigonometric functions so that the calculations would be fast. My iPhone 5 will do 10,000 points in 20 milliseconds, and 100,000 points in 150 milliseconds. You can do a million points in 1.5 seconds, but at that point you'd need a throbber. Disclosure as per the rules: I built it.
From the Kerf REPL:
//ARTIFICIAL POINT GENERATION ///////////////////
n: 10**4
point_lat: 80 + rand(40.0)
point_lon: 80 + rand(40.0)
mytable: {{lats: 60 + rand(n, 60.0), lons: 60 + rand(n, 60.0)}}
lats : mytable.lats
lons : mytable.lons
/////////////////////////////////////
//COMPUTATION////////////////////////
dlon: lons - point_lon
dlat: lats - point_lat
distances_km: (6378.137 * 2) * asin(mins(1,sqrt(pow(sin(dlat/2),2) + cos(point_lat) * cos(lats) * pow(sin(dlon/2) ,2))))
//distances_miles: 0.621371192 * distances_km //km to miles [statute] conversion
//sort_order: ascend distances_km
Or via the Kerf iOS SDK. Removing the semicolon at the end of a statement will allow you to log it as JSON to the terminal.
KSKerfSDK *kerf = [KSKerfSDK new];
kerf.showTimingEnabled = YES;
//Sample Data Generation
[kerf jsonObjectFromCall:#"n: 10**4;"];
[kerf jsonObjectFromCall:#"point_lat: 80 + rand(40.0);"];
[kerf jsonObjectFromCall:#"point_lon: 80 + rand(40.0);"];
[kerf jsonObjectFromCall:#"mytable: {{lats: 60 + rand(n, 60.0), lons: 60 + rand(n, 60.0)}};"];
[kerf jsonObjectFromCall:#"lats : mytable.lats;"];
[kerf jsonObjectFromCall:#"lons : mytable.lons;"];
//Computation
[kerf jsonObjectFromCall:#"dlon: lons - point_lon;"];
[kerf jsonObjectFromCall:#"dlat: lats - point_lat;"];
NSLog(#"%#", [kerf jsonObjectFromCall:#"distances_km: (6378.137 * 2) * asin(mins(1,sqrt(pow(sin(dlat/2),2) + cos(point_lat) * cos(lats) * pow(sin(dlon/2) ,2)))); "]);

To test whether points (x,y) are inside a circle with center (cx,cy) and radius r, use the equation (x-cx)² + (y-cy)² <= r².
This does not correspond to circle because longitude and latitude values do not have the same length in the earth's surface, but it's near enough.
In SQL:
... WHERE (longitude - :lon) * (longitude - :lon) +
(latitude - :lat) * (latitude - :lat) <= :r * :r
If you use a rectangle instead, you can use simpler expressions that have a chance of being optimized with an index:
... WHERE longitude BETWEEN :XMin AND :XMax
AND latitude BETWEEN :YMin AND :YMax

Related

Adding ease in and out to moving platforms with spritekit for 2d platformer

Well Hello,
I'm making a 2d platformer for iOS using spritekit. I have moving platforms to allow my characters to move with the platform.
I can't just use skactions to move my platforms because the character will not move with the platform.
question:
How would I add an ease in and out function in order to have the platforms??? simulate: SKactionTimeMode.easeInEaseOut
Current Solution:
I don't have the code in front of me, but for a left/right moving platform this is pretty much what I'm doing. This would be running within the platforms update() method.
If platform.position.x < xPositionIWantNodeToStopGoingLeft {
velAmount = -velAmount
}
else if platform.position.x > xPositionIWantNodeToStopGoingRight {
velAmount = -velAmount
}
platform.physicsBody?.velocity = SKVector(dx: velAmount, dy: velAmount
platform.position.y = staticYPosition
Just to clarify, this works great. If there is a better way to do this I'm all ears. But this creates a jagged stop and turn kind of feel. I want that ease in and out feel so that the platform feels more natural.
Thanks for any help!!!
Ease in out function
If we consider the time for the platform to move from one side to the other as one unit ( it might be 10 seconds, or 17 frames, it does not matter, we work in units for now).
We do the same with the distance. The platform must move one unit distance in one unit of time.
For this answer time is t and the position is a function of time written as f(t) is the platform position at time t.
For simple linear movement then the function is simply f(t)=t. So at time t=0 the distance moved is 0, at time 0.5 (half way) the distance is 0.5 (half way), and so on.
So lets put that into something a little more practical.
Please excuse my swift I have never used it befor (I am sure you can correct any syntax I get wrong).
// First normalise the distance and time (make them one unit long)
// get the distance
let distance = Double(xPositionStopGoingLeft - xPositionStopGoingRight);
// use that and the velocity to get the time to travel
let timeToTravel = distance / Double(velAmountX);
// first we have a frame ticker
gameTick += 1; // that ticks for every frame
// We can assume that the platform is always moving back and forth
// Now is the unit time where at now = 2 the platform has move there and back
// at 3 it has move across again and at 4 back again.
let now = Double(gameTick) / timeToTravel; // normalize time.
// get the remainder of 2 as from 0-1 is moving forward and 1-2 is back
let phase = now % 2.0;
// We also need the unit time for the function f(t)=t
let t = abs(phase - 1);
if phase >= 1 { t = 1 - t } // reverse for return
// implement the function f(t) = t where f(t) is dist
let dist = t
// and convert back to pixel distance
platform.position.x = Int(dist * distance + Double(xPositionStopGoingLeft));
So that is the linear platform. To make the movement change all we need to do is change the function f(t)=?, in the above its the line let dist = t
For a ease in out there is a handy function that is used in most ease applications f(t) = t * t / ((t * t) + (1 - t) * ( 1 - t))
There are some t*t which are powers, t to the power of 2 or t^2 . In swift its pow(t,2) so rewriting the above as code
let dist = pow(t,2) / (pow(t,2) + pow((1-t),2);
This gives a nice ease at the start and end As the distance and time traveled is constant the speed at the middle point t = 0.5 must be greater to catch up with the slow start and end. (Side note, Get the derivative of the above function lets you workout the speed at every point in time f'(t) = speed(t) = 2(-(t-1)t)^(2-1) /(t^2+(1-t)^2)^2)
This function is so nice, the speed at time 0.5 is 2, the same as the power (for the linear journey it would be 1). A handy property of the function is that the speed at the mid way point is always the same as the power. If you want it to move really fast at the midpoint say 4 times as fast then you use the power of 4
let dist = pow(t,4) / (pow(t,4) + pow((1-t),4);
If you want it only to speed up a little say 1.2 times the speed at the center then the power is 1.2
let dist = pow(t,1.2) / (pow(t,1.2) + pow((1-t),1.2);
So now we can introduce another term, maxSpeed which is the normalised maxSpeed (Side note more precisely it is the speed at t=0.5 as it can be the slower than 1, but for our need max speed will do)
let maxSpeed = Double(velAmountX + 3) / Double(velAmountX); // 3 pixels per frame faster
and the function f(t) = t^m / (t^m + (1-t)^m) where m is maxSpeed.
and as code
let dist = pow(t,maxSpeed ) / (pow(t,maxSpeed ) + pow((1-t),maxSpeed);
So put that all together
// the next 3 lines can be constats
let distance = Double(xPositionStopGoingLeft - xPositionStopGoingRight);
let timeToTravel = distance / Double(velAmountX);
let maxSpeed = Double(velAmountX + 3) / Double(velAmountX);
gameTick += 1; // that ticks for every frame
let now = Double(gameTick) / timeToTravel; // normalize time.
let phase = now % 2.0;
let t = abs(phase - 1);
if phase >= 1 { t = 1 - t } // reverse for return
// the next line is the ease function
let dist = pow(t, maxSpeed) / (pow(t, maxSpeed) + pow((1-t) ,maxSpeed);
// position the platform
platform.position.x = Int(dist * distance + Double(xPositionStopGoingLeft));
Now you can at any tick calculate the position of the platform. If you want to slow the whole game down and step frames at half ticks it still will work. if you speed the game up gameTick += 2 it still works.
Also the max speed can be lower than the linear speed. If you want the platform to be half the normal speed at the center t=0.5 the set maxSpeed = 0.5 and at the halfway point the speed will be half. To keep everything working the ease at the start and end will be quicker a rush in and rush out. (and works for reverse as well)
To help maybe a visual representation
Image shows the movement of the platform back and forth over time. The distance is about 60 pixels and the time can be 1 minute. So at 1 min it will be one the right 2min on the left, and so on.
Then we normalise the movement and time by looking only at one section of movement.
The graph represents the movement from left to right side, the distance is 1, and the time is 1. It has just been scaled to fit the unit box (1 by 1 box).
The red line represent the linear movement f(t)=t (constant speed). At any point of time you move across hit the line move down and you can find the distance traveled.
The green line represents the ease function f(t)=t*t/(t*t+(1-t)*(1-t)) and it works the same. At any point of time scan across to find the green line and move down to get the distance. the function f(t) does that for you.
With the maxSpeed the steepness of the line at dist 0.5 is changed, with steeper slope representing faster travel.
For physic, play with friction and linear damping of the body. You can even use an SKAction run block to reduce or add friction.
you could do something like:
physicsBody.friction = (10 - physicsBody.velocity.dx) > 0 ? (10 - physicsBody.velocity.dx) / 10 : 0
Basically it gives friction when velocity.dx is < 10, you may want to tweak the 10 to the number of your liking

Not enough precision for points in CGPoint type

I'm developing map-based game using cocos2d v3.
I have a map with size of 2^19 points. On that map I have object that should move over time in short distance. About 60-70 points.
CGPoint offset = [_trajectoryPath offsetForNextPosition];
CGFloat x = self.position.x + offset.x;
CGFloat y = self.position.y + offset.y;
self.position = CGPointMake(x, y);
At such map size map position can be something like {300000, 40000} points.
When I try to add small step, lets say about {0.002f, 0.004f}, to animate object position I end up with still the same {300000, 40000} points...
I understand that it happens because of precision of float. Values normalised by map size, to be between 0 and 1.0, don't work either.
Is it possible somehow to increase precision of float type on iOS? Or may be someone cam give a hint about possible workaround for this problem?
Thanks.
mightee.cactus, I remember we had a similar issue while adding very small numbers to very large ones with float in c.
The solution was follows: we changed types to double to preserve accuracy; in your case you can make all the arithmetic operations with doubles and translate them into CGFloat just before use in CGPointMake.

transform longitude latitude into meters

I need a function that maps gps positions to x/y values like this:
getXYpos(GeoPoint relativeNullPoint, GeoPoint p){
deltaLatitude=p.latitude-relativeNullPoint.latitude;
deltaLongitude=p.longitude-relativeNullPoint.longitude;
...
resultX=latitude (or west to east) distance in meters from p to relativeNullPoint
resultY=longitude (or south to north) distance in meters from p to relativeNullPoint
}
i have seen some implementations of "distance of two geoPoints" but they all just calculate the air-line distance.
i think the deltaLongitude can be transformed into meters directly but the deltaLatitude depends in the Longitude. does anyone know how this problem can be solved?
To start with, I think you have your latitude and longitude reversed. Longitude measures X, and latitude measures Y.
The latitude is easy to turn into a north-south distance. We know that 360 degrees is a full circle around the earth through the poles, and that distance is 40008000 meters. As long as you don't need to account for the errors due to the earth being not perfectly spherical, the formula is deltaLatitude * 40008000 / 360.
The tricky part is converting longitude to X, as you suspected. Since it depends on the latitude you need to decide which latitude you're going to use - you could choose the latitude of your origin, the latitude of your destination, or some arbitrary point in between. The circumference at the equator (latitude 0) is 40075160 meters. The circumference of a circle at a given latitude will be proportional to the cosine, so the formula will be deltaLongitude * 40075160 * cos(latitude) / 360.
Edit: Your comment indicates you had some trouble with the longitude formula; you might have used degrees instead of radians in the call to cos, that's a common rookie mistake. To make sure there's no ambiguity, here's working code in Python.
def asRadians(degrees):
return degrees * pi / 180
def getXYpos(relativeNullPoint, p):
""" Calculates X and Y distances in meters.
"""
deltaLatitude = p.latitude - relativeNullPoint.latitude
deltaLongitude = p.longitude - relativeNullPoint.longitude
latitudeCircumference = 40075160 * cos(asRadians(relativeNullPoint.latitude))
resultX = deltaLongitude * latitudeCircumference / 360
resultY = deltaLatitude * 40008000 / 360
return resultX, resultY
I chose to use the relativeNullPoint latitude for the X calculation. This has the benefit that if you convert multiple points with the same longitude, they'll have the same X; north-south lines will be vertical.
Edit again: I should have pointed out that this is a very simple formula and you should know its limitations. Obviously the earth is not flat, so any attempt to map it to XY coordinates will involve some compromises. The formula I derived above works best when the area you're converting is small enough to consider flat, and where the slight curvature and non-parallelism of north-south lines can be ignored. There's a whole science to map projections; if you want to see some possibilities a good place to start would be Wikipedia. This specific projection is known as the Equirectangular projection, with some added scaling.
Harvesine function is what you need.
Check it out at moveable-types There are Distance, Bearing, Midpoint and other stuff Javascript implementation working really good.
UPDATE
I've found a Java implementation of Harvesine function in another stackoverflow question
There are libraries on jstott.me.uk for PHP, Java and Javascript which do this, e.g.
var lld1 = new LatLng(40.718119, -73.995667); // New York
document.write("New York Lat/Long: " + lld1.toString() + "<br />");
var lld2 = new LatLng(51.499981, -0.125313); // London
document.write("London Lat/Long: " + lld2.toString() + "<br />");
var d = lld1.distance(lld2);
document.write("Surface Distance between New York and London: " + d + "km");

geo-indexing: efficiently calculating proximity based on latitude/longitude

My simple web app (WSGI, Python) supports text queries to find items in the database.
Now I'd like to extend this to allow for queries like "find all items within 1 mile of {lat,long}".
Of course that's a complex job if efficiency is a concern, so I'm thinking of a dedicated external module that does indexing for geo-coordinates - sort of like Lucene would for text.
I assume a generic component like this already exists, but haven't been able to find anything so far. Any help would be greatly appreciated.
Have you checked out mongo db, they have a geo indexing feature. http://www.mongodb.org/display/DOCS/Geospatial+Indexing
I could only think of a semi-brute-force attack if you plan to implement it directly with Python, which I already did with similar purposes:
#!/usr/bin/python
from math import *
def distance(p1,p2): # uses the haversine function and an ellipsoid model
lat1, long1 = p1; lat2, long2 = p2
lat1=radians(lat1); long1=radians(long1); lat2=radians(lat2); long2=radians(long2)
maior=6378.137; menor=6356.7523142
R=(maior*menor)/sqrt((maior*cos(lat1))**2 + (menor*sin(lat1))**2)
d_lat = lat2 - lat1; d_long = long2 - long1
a = sin(d_lat/2)**2 + cos(lat1) * cos(lat2) * sin(d_long/2)**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
length = R * c
x = sin(d_long) * cos(lat2)
y = cos(lat2) * sin(lat1) - sin(lat2) * cos (lat1) * cos(d_long)
bearing = 90-(degrees(atan2(y, -x)))
return length, bearing
For the screening of points for distance, you can first find candidate points whose "x" and "y" coordinates are inside a square centered on your testing position (much faster) and just then test for actual geodesic distance.
Hope it helps!

How do I calculate the Azimuth (angle to north) between two WGS84 coordinates

I have got two WGS84 coordinates, latitude and longitude in degrees. These points are rather close together, e.g. only one metre apart.
Is there an easy way to calculate the azimuth of the line between these points, that is, the angle to north?
The naive approach would be to assume a Cartesian coordinate system (because these points are so close together) and just use
sin(a) = abs(L2-L1) / sqrt(sqr(L2-L1) + sqr(B2-B1))
a = azimuth
L1, L2 = longitude
B1, B2 = latitude
The error will be larger as the coordinates move away from the equator because there the distance between two longitudinal degrees becomes increasingly smaller than the one between two latitudinal degrees (which remains constant).
I found some quite complex formulas which I don't really want to implement because they seem to be overkill for points that are that close together and I don't need very high precision (two decimals are enough, one is probably fine either since there are other factors that reduce precision anyway, like the one the GPS returns).
Maybe I could just determine an approximate longitudinal correction factor depending on latitude and use somthing like this:
sin(a) = abs(L2*f-L1*f) / sqrt(sqr(L2*f-L1*f) + sqr(B2-B1))
where f is the correction factor
Any hints?
(I don't want to use any libraries for this, especially not ones that require runtime licenses. Any MPLed Delphi Source would be great.)
The formulas that you refer to in the text are to calculate the great circle distance between 2 points. Here's how I calculate the angle between points:
uses Math, ...;
...
const
cNO_ANGLE=-999;
...
function getAngleBetweenPoints(X1,Y1,X2,Y2:double):double;
var
dx,dy:double;
begin
dx := X2 - X1;
dy := Y2 - Y1;
if (dx > 0) then result := (Pi*0.5) - ArcTan(dy/dx) else
if (dx < 0) then result := (Pi*1.5) - ArcTan(dy/dx) else
if (dy > 0) then result := 0 else
if (dy < 0) then result := Pi else
result := cNO_ANGLE; // the 2 points are equal
result := RadToDeg(result);
end;
Remember to handle the situation where 2 points are equal (check if the result equals cNO_ANGLE, or modify the function to throw an exception);
This function assumes that you're on a flat surface. With the small distances that you've mentioned this is all fine, but if you're going to be calculating the heading between cities all over the world you might want to look into something that takes the shape of the earth in count;
It's best to provide this function with coordinates that are already mapped to a flat surface. You could feed WGS84 Latitude directly into Y (and lon into X) to get a rough approximation though.
Here is the C# solution. Tested for 0, 45, 90, 135, 180, 225, 270 and 315 angles.
Edit I replaced my previous ugly solution, by the C# translation of Wouter's solution:
public double GetAzimuth(LatLng destination)
{
var longitudinalDifference = destination.Lng - this.Lng;
var latitudinalDifference = destination.Lat - this.Lat;
var azimuth = (Math.PI * .5d) - Math.Atan(latitudinalDifference / longitudinalDifference);
if (longitudinalDifference > 0) return azimuth;
else if (longitudinalDifference < 0) return azimuth + Math.PI;
else if (latitudinalDifference < 0) return Math.PI;
return 0d;
}
public double GetDegreesAzimuth(LatLng destination)
{
return RadiansToDegreesConversionFactor * GetAzimuth(destination);
}
I found this link
http://williams.best.vwh.net/avform.htm
given in the answer to
Lat/Lon + Distance + Heading --> Lat/Lon
This looks promising, especially the flat earth approximation given near the end.
This would work only for small differences. Otherwise you can't just "latitudinalDifference / longitudinalDifference".
I would recommend implementing a correction factor based on the longitude. I implemented a simular routine once to return all geocoded records within x miles of a specific spot and ran into simular issues. Unfortunately I don't have the code anymore, and can't seem to recall how I got to the correction number but you are on the right track.
Has anyone tested this? It does not return the correct answers
This function assumes that you're on a flat surface. With the small distances that you've mentioned this is all fine, but if you're going to be calculating the heading between cities all over the world you might want to look into something that takes the shape of the earth in count;
Your flat earth has little to do with this. The error, as you call it, is because you are calculating an initial azimuth from a point. Unless you are heading straight to a pole, you relationship to the pole will change with distance. Regardless, the above program does not return correct results.

Resources