Calculating bearing between two CLLocationCoordinate2Ds - ios

Very "simple" problem: given two CLLocationCoordinate2Ds, how can I get the bearing (as radians) from the first to the second? I've done a lot of research and studying on this, both the general problem and Objective-C/Cocoa Touch/iOS specifically.
Here's my implementation:
- (float) getHeadingForDirectionFromCoordinate:(CLLocationCoordinate2D)fromLoc toCoordinate:(CLLocationCoordinate2D)toLoc
{
float fLat = fromLoc.latitude;
float fLng = fromLoc.longitude;
float tLat = toLoc.latitude;
float tLng = toLoc.longitude;
return atan2(sin(fLng-tLng)*cos(tLat), cos(fLat)*sin(tLat)-sin(fLat)*cos(tLat)*cos(fLng-tLng));
}
However, this method isn't returning consistant results for me. If the bearing is close to due north or due south, it seems to be fine, however, any other direction seems to return inconsistant data, for example:
From 50.405018, 8.437500
To 51.339802, 12.403340
My method returns: 5.918441 radians
Should be 1.18660576 radians
(see http://www.movable-type.co.uk/scripts/latlong.html and http://www.movable-type.co.uk/scripts/latlong-map.html?lat1=50.405018&long1=8.437500&lat2=51.339802&long2=12.403340)
I've double and triple checked the formula is correct. I've also spot checked a bunch of values like the example above, some correct, some wrong. I've played around with various modulos or bounding of the return value, also no luck.
Any ideas? Is there an issue with my code? Maybe I've misunderstood something about how math functions work?

Here the code modified with the changes suggested by Oren Trutner and from myself:
#define degreesToRadians(x) (M_PI * x / 180.0)
#define radiansToDegrees(x) (x * 180.0 / M_PI)
- (float)getHeadingForDirectionFromCoordinate:(CLLocationCoordinate2D)fromLoc toCoordinate:(CLLocationCoordinate2D)toLoc
{
float fLat = degreesToRadians(fromLoc.latitude);
float fLng = degreesToRadians(fromLoc.longitude);
float tLat = degreesToRadians(toLoc.latitude);
float tLng = degreesToRadians(toLoc.longitude);
float degree = radiansToDegrees(atan2(sin(tLng-fLng)*cos(tLat), cos(fLat)*sin(tLat)-sin(fLat)*cos(tLat)*cos(tLng-fLng)));
if (degree >= 0) {
return degree;
} else {
return 360+degree;
}
}

Your math is correct, with the following exceptions:
Make sure to convert fLat, fLon, tLat, and tLon to radians before applying any sin() or cos() to them. Divide by 180.0 and multiply by PI.
Enter the delta between tLng and fLng as tLng-fLng, and not the other way around. Note that this difference appears twice in the expression.
With those changes, I am getting 1.18660677830947 radians with double precision math and the values in the question.

Swift 3:
extension CLLocationCoordinate2D {
func bearing(to point: CLLocationCoordinate2D) -> Double {
func degreesToRadians(_ degrees: Double) -> Double { return degrees * Double.pi / 180.0 }
func radiansToDegrees(_ radians: Double) -> Double { return radians * 180.0 / Double.pi }
let lat1 = degreesToRadians(latitude)
let lon1 = degreesToRadians(longitude)
let lat2 = degreesToRadians(point.latitude);
let lon2 = degreesToRadians(point.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(radiansBearing)
}
}

you can use my code.. it's work on my project with microcontroller that use GPS for data.
#define d2r ((22/7.0)/180.0)
#define r2d (180.0/(22/7.0))
double get_heading1(double lat1, double long1, double lat2, double long2)
{
double diff_lat, diff_long;
double degree;
diff_long =(double) (((long2*1000000)-(long1*1000000))/1000000) * d2r;
diff_lat = (double) (((lat2*1000000)-(lat1*1000000))/1000000) * d2r;
degree = r2d (atan2(sin(diff_long)*cos(d2r*lat2),cos(d2r*lat1)*sin(d2r*lat2)-sin(d2r*lat1)*cos(d2r*lat2) *cos(diff_long)));
if (degree >= 0) {
return degree;
} else {
return 360+degree;
}
}

Related

How to get buffer polygon coordinates (latitude and longitude) in iOS?

I have created one polygon on map with some set of coordinates.
I need help regarding making one buffered polygon with some given distance outside of original polygon border.
so what i need a method with such algorithm in which i pass set of coordinates as input and should get buffered set of coordinates as output.
I tried to achieve this by using arcgis library for ios with bufferGeometry method of AGSGeometryEngine but problem is, this is tightly coupled and only will work their GIS Map but I am using Mapbox which is different Map. So I want one generic method which can resolve my problem independent to map.
The solution of #Ravikant Paudel though comprehensive didn't work for me so I have implemented the approach myself.
Also, I implemented the approach in kotlin and adding it here so that someone else who is facing a similar problem will find it helpful.
Approach:
Find the angle of the angle bisector theta for every vertice of the polygon.
Draw a circle with radius = bufferedDistance / sin(angleBisctorTheta)
Find intersections of the circle and angle bisector.
Out of the 2 intersection points the one inside the polygon will give you the buffered vertice for the shrunk polygon and the outside point for the buffered polygon.
This approach does not account for the corner cases in which both points somehow fall inside or outside the polygon -> in which case the buffered polygon formed will be malformed.
Code:
private fun computeAngleBisectorTheta(
prevLatLng: LatLng,
currLatLng: LatLng,
nextLatLng: LatLng
): Double {
var phiBisector = 0.0
try {
val aPrime = getDeltaPrimeVector(prevLatLng, currLatLng)
val cPrime = getDeltaPrimeVector(nextLatLng, currLatLng)
val thetaA = atan2(aPrime[1], aPrime[0])
val thetaC = atan2(cPrime[1], cPrime[0])
phiBisector = (thetaA + thetaC) / 2.0
} catch (e: Exception) {
logger.error("[Error] in computeAngleBisectorSlope: $e")
}
return phiBisector
}
private fun getDeltaPrimeVector(
aLatLng: LatLng,
bLatLng: LatLng
): ArrayList<Double> {
val arrayList: ArrayList<Double> = ArrayList<Double>(2)
try {
val aX = convertToXY(aLatLng.latitude)
val aY = convertToXY(aLatLng.longitude)
val bX = convertToXY(bLatLng.latitude)
val bY = convertToXY(bLatLng.longitude)
arrayList.add((aX - bX))
arrayList.add((aY - bY))
} catch (e: Exception) {
logger.error("[Error] in getDeltaPrimeVector: $e")
}
return arrayList
}
private fun convertToXY(coordinate: Double) =
EARTH_RADIUS * toRad(coordinate)
private fun convertToLatLngfromXY(coordinate: Double) =
toDegrees(coordinate / EARTH_RADIUS)
private fun computeBufferedVertices(
angle: Double, bufDis: Int,
centerLatLng: LatLng
): ArrayList<LatLng> {
var results = ArrayList<LatLng>()
try {
val distance = bufDis / sin(angle)
var slope = tan(angle)
var inverseSlopeSquare = sqrt(1 + slope * slope * 1.0)
var distanceByInverseSlopeSquare = distance / inverseSlopeSquare
var slopeIntoDistanceByInverseSlopeSquare = slope * distanceByInverseSlopeSquare
var p1X: Double = convertToXY(centerLatLng.latitude) + distanceByInverseSlopeSquare
var p1Y: Double =
convertToXY(centerLatLng.longitude) + slopeIntoDistanceByInverseSlopeSquare
var p2X: Double = convertToXY(centerLatLng.latitude) - distanceByInverseSlopeSquare
var p2Y: Double =
convertToXY(centerLatLng.longitude) - slopeIntoDistanceByInverseSlopeSquare
val tempLatLng1 = LatLng(convertToLatLngfromXY(p1X), convertToLatLngfromXY(p1Y))
results.add(tempLatLng1)
val tempLatLng2 = LatLng(convertToLatLngfromXY(p2X), convertToLatLngfromXY(p2Y))
results.add(tempLatLng2)
} catch (e: Exception) {
logger.error("[Error] in computeBufferedVertices: $e")
}
return results
}
private fun getVerticesOutsidePolygon(
verticesArray: ArrayList<LatLng>,
polygon: ArrayList<LatLng>
): LatLng {
if (isPointInPolygon(
verticesArray[0].latitude,
verticesArray[0].longitude,
polygon
)
) {
if (sPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
logger.error("[ERROR] Malformed polygon! Both Vertices are inside the polygon! $verticesArray")
} else {
return verticesArray[1]
}
} else {
if (PolygonGeofenceHelper.isPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
return verticesArray[0]
} else {
logger.error("[ERROR] Malformed polygon! Both Vertices are outside the polygon!: $verticesArray")
}
}
//returning a vertice anyway because there is no fall back policy designed if both vertices are inside or outside the polygon
return verticesArray[0]
}
private fun toRad(angle: Double): Double {
return angle * Math.PI / 180
}
private fun toDegrees(radians: Double): Double {
return radians * 180 / Math.PI
}
private fun getVerticesInsidePolygon(
verticesArray: ArrayList<LatLng>,
polygon: ArrayList<LatLng>
): LatLng {
if (isPointInPolygon(
verticesArray[0].latitude,
verticesArray[0].longitude,
polygon
)
) {
if (isPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
logger.error("[ERROR] Malformed polygon! Both Vertices are inside the polygon! $verticesArray")
} else {
return verticesArray[0]
}
} else {
if (PolygonGeofenceHelper.isPointInPolygon(
verticesArray[1].latitude,
verticesArray[1].longitude,
polygon
)
) {
return verticesArray[1]
} else {
logger.error("[ERROR] Malformed polygon! Both Vertices are outside the polygon!: $verticesArray")
}
}
//returning a vertice anyway because there is no fall back policy designed if both vertices are inside or outside the polygon
return LatLng(0.0, 0.0)
}
fun getBufferedPolygon(
polygon: ArrayList<LatLng>,
bufferDistance: Int,
isOutside: Boolean
): ArrayList<LatLng> {
var bufferedPolygon = ArrayList<LatLng>()
var isBufferedPolygonMalformed = false
try {
for (i in 0 until polygon.size) {
val prevLatLng: LatLng = polygon[if (i - 1 < 0) polygon.size - 1 else i - 1]
val centerLatLng: LatLng = polygon[i]
val nextLatLng: LatLng = polygon[if (i + 1 == polygon.size) 0 else i + 1]
val computedVertices =
computeBufferedVertices(
computeAngleBisectorTheta(
prevLatLng, centerLatLng, nextLatLng
), bufferDistance, centerLatLng
)
val latLng = if (isOutside) {
getVerticesOutsidePolygon(
computedVertices,
polygon
)
} else {
getVerticesInsidePolygon(
computedVertices,
polygon
)
}
if (latLng.latitude == 0.0 && latLng.longitude == 0.0) {
isBufferedPolygonMalformed = true
break
}
bufferedPolygon.add(latLng)
}
if (isBufferedPolygonMalformed) {
bufferedPolygon = polygon
logger.error("[Error] Polygon generated is malformed returning the same polygon: $polygon , $bufferDistance, $isOutside")
}
} catch (e: Exception) {
logger.error("[Error] in getBufferedPolygon: $e")
}
return bufferedPolygon
}
You'll need to pass an array of points present in the polygon in the code and the buffer distance the third param is to get the outside buffer or the inside buffer. (Note: I am assuming that the vertices in this list are adjacent to each other).
I have tried to keep this answer as comprehensive as possible. Please feel free to suggest any improvements or a better approach.
You can find the detailed math behind the above code on my portfolio page.
Finding angle bisector
To approximate latitude and longitude to a 2D cartesian coordinate system.
To check if the point is inside a polygon I am using the approach mentioned in this geeks for geeks article
I have same problem in my app and finally found the solution by the help of this site
I am an android developer and my code may not be useful to you but the core concept is same.
At first we need to find the bearing of the line with the help of two points LatLng points.(i have done by using computeDistanceAndBearing(double lat1, double lon1,double lat2, double lon2) function)
Now to get the buffering of certain point we need to give the buffering distance ,LatLng point and bearing (which i obtain from computeDistanceAndBearing function).(I have done this by using computeDestinationAndBearing(double lat1, double lon1,double brng, double dist) function ). from single LatLng point we get two points by producing them with their bearing with certain distance.
Now we need to find the interestion point of the two point to get the buffering that we want. for this remember to take new obtain point and bearing of another line and same with another. This helps to obtain new intersection point with buffering you want.(i have done this in my function computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2))
Do this to all the polygon points and then you get new points whichyou joint to get buffering.
This is the way i have done in my android location app whis is
Here is my code
//computeDistanceAndBearing(double lat1, double lon1,
double lat2, double lon2)
public static double[] computeDistanceAndBearing(double lat1, double lon1,
double lat2, double lon2) {
// Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
// using the "Inverse Formula" (section 4)
double results[] = new double[3];
int MAXITERS = 20;
// Convert lat/long to radians
lat1 *= Math.PI / 180.0;
lat2 *= Math.PI / 180.0;
lon1 *= Math.PI / 180.0;
lon2 *= Math.PI / 180.0;
double a = 6378137.0; // WGS84 major axis
double b = 6356752.3142; // WGS84 semi-major axis
double f = (a - b) / a;
double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
double L = lon2 - lon1;
double A = 0.0;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
double cosU1 = Math.cos(U1);
double cosU2 = Math.cos(U2);
double sinU1 = Math.sin(U1);
double sinU2 = Math.sin(U2);
double cosU1cosU2 = cosU1 * cosU2;
double sinU1sinU2 = sinU1 * sinU2;
double sigma = 0.0;
double deltaSigma = 0.0;
double cosSqAlpha = 0.0;
double cos2SM = 0.0;
double cosSigma = 0.0;
double sinSigma = 0.0;
double cosLambda = 0.0;
double sinLambda = 0.0;
double lambda = L; // initial guess
for (int iter = 0; iter < MAXITERS; iter++) {
double lambdaOrig = lambda;
cosLambda = Math.cos(lambda);
sinLambda = Math.sin(lambda);
double t1 = cosU2 * sinLambda;
double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
double sinSqSigma = t1 * t1 + t2 * t2; // (14)
sinSigma = Math.sqrt(sinSqSigma);
cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
sigma = Math.atan2(sinSigma, cosSigma); // (16)
double sinAlpha = (sinSigma == 0) ? 0.0 : cosU1cosU2 * sinLambda
/ sinSigma; // (17)
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SM = (cosSqAlpha == 0) ? 0.0 : cosSigma - 2.0 * sinU1sinU2
/ cosSqAlpha; // (18)
double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
A = 1 + (uSquared / 16384.0) * // (3)
(4096.0 + uSquared * (-768 + uSquared * (320.0 - 175.0 * uSquared)));
double B = (uSquared / 1024.0) * // (4)
(256.0 + uSquared * (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
double C = (f / 16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
double cos2SMSq = cos2SM * cos2SM;
deltaSigma = B
* sinSigma
* // (6)
(cos2SM + (B / 4.0)
* (cosSigma * (-1.0 + 2.0 * cos2SMSq) - (B / 6.0) * cos2SM
* (-3.0 + 4.0 * sinSigma * sinSigma)
* (-3.0 + 4.0 * cos2SMSq)));
lambda = L
+ (1.0 - C)
* f
* sinAlpha
* (sigma + C * sinSigma
* (cos2SM + C * cosSigma * (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
double delta = (lambda - lambdaOrig) / lambda;
if (Math.abs(delta) < 1.0e-12) {
break;
}
}
double distance = (b * A * (sigma - deltaSigma));
results[0] = distance;
if (results.length > 1) {
double initialBearing = Math.atan2(cosU2 * sinLambda, cosU1 * sinU2
- sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
results[1] = initialBearing;
if (results.length > 2) {
double finalBearing = Math.atan2(cosU1 * sinLambda, -sinU1 * cosU2
+ cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
results[2] = finalBearing;
}
}
return results;
}
//computeDestinationAndBearing(double lat1, double lon1,double brng, double dist)
public static double[] computeDestinationAndBearing(double lat1, double lon1,
double brng, double dist) {
double results[] = new double[3];
double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
// ellipsiod
double s = dist;
double alpha1 = toRad(brng);
double sinAlpha1 = Math.sin(alpha1);
double cosAlpha1 = Math.cos(alpha1);
double tanU1 = (1 - f) * Math.tan(toRad(lat1));
double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
double sigma1 = Math.atan2(tanU1, cosAlpha1);
double sinAlpha = cosU1 * sinAlpha1;
double cosSqAlpha = 1 - sinAlpha * sinAlpha;
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384
* (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
double sinSigma = 0, cosSigma = 0, deltaSigma = 0, cos2SigmaM = 0;
double sigma = s / (b * A), sigmaP = 2 * Math.PI;
while (Math.abs(sigma - sigmaP) > 1e-12) {
cos2SigmaM = Math.cos(2 * sigma1 + sigma);
sinSigma = Math.sin(sigma);
cosSigma = Math.cos(sigma);
deltaSigma = B
* sinSigma
* (cos2SigmaM + B
/ 4
* (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6
* cos2SigmaM * (-3 + 4 * sinSigma * sinSigma)
* (-3 + 4 * cos2SigmaM * cos2SigmaM)));
sigmaP = sigma;
sigma = s / (b * A) + deltaSigma;
}
double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1,
(1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1
* sinSigma * cosAlpha1);
double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
double L = lambda
- (1 - C)
* f
* sinAlpha
* (sigma + C * sinSigma
* (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
double lon2 = (toRad(lon1) + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise
// to
// -180...+180
double revAz = Math.atan2(sinAlpha, -tmp); // final bearing, if required
results[0] = toDegrees(lat2);
results[1] = toDegrees(lon2);
results[2] = toDegrees(revAz);
return results;
}
private static double toRad(double angle) {
return angle * Math.PI / 180;
}
private static double toDegrees(double radians) {
return radians * 180 / Math.PI;
}
//computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2)
public static LatLng computeIntersectionPoint(LatLng p1, double brng1, LatLng p2, double brng2) {
double lat1 = toRad(p1.latitude), lng1 = toRad(p1.longitude);
double lat2 = toRad(p2.latitude), lng2 = toRad(p2.longitude);
double brng13 = toRad(brng1), brng23 = toRad(brng2);
double dlat = lat2 - lat1, dlng = lng2 - lng1;
double delta12 = 2 * Math.asin(Math.sqrt(Math.sin(dlat / 2) * Math.sin(dlat / 2)
+ Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlng / 2) * Math.sin(dlng / 2)));
if (delta12 == 0) return null;
double initBrng1 = Math.acos((Math.sin(lat2) - Math.sin(lat1) * Math.cos(delta12)) / (Math.sin(delta12) * Math.cos(lat1)));
double initBrng2 = Math.acos((Math.sin(lat1) - Math.sin(lat2) * Math.cos(delta12)) / (Math.sin(delta12) * Math.cos(lat2)));
double brng12 = Math.sin(lng2 - lng1) > 0 ? initBrng1 : 2 * Math.PI - initBrng1;
double brng21 = Math.sin(lng2 - lng1) > 0 ? 2 * Math.PI - initBrng2 : initBrng2;
double alpha1 = (brng13 - brng12 + Math.PI) % (2 * Math.PI) - Math.PI;
double alpha2 = (brng21 - brng23 + Math.PI) % (2 * Math.PI) - Math.PI;
double alpha3 = Math.acos(-Math.cos(alpha1) * Math.cos(alpha2) + Math.sin(alpha1) * Math.sin(alpha2) * Math.cos(delta12));
double delta13 = Math.atan2(Math.sin(delta12) * Math.sin(alpha1) * Math.sin(alpha2), Math.cos(alpha2) + Math.cos(alpha1) * Math.cos(alpha3));
double lat3 = Math.asin(Math.sin(lat1) * Math.cos(delta13) + Math.cos(lat1) * Math.sin(delta13) * Math.cos(brng13));
double dlng13 = Math.atan2(Math.sin(brng13) * Math.sin(delta13) * Math.cos(lat1), Math.cos(delta13) - Math.sin(lat1) * Math.sin(lat3));
double lng3 = lng1 + dlng13;
return new LatLng(toDegrees(lat3), (toDegrees(lng3) + 540) % 360 - 180);
}
I will suggest you to go through the the above site and get the knowledge as i had also done the same.
Hope this may help , i know the is not in ios but the concept is same as i done my project by changing code of javascript.
Cheers !!!
My requirement was something similar to this. I ended up writing up my own algo for this. https://github.com/RanaRanvijaySingh/PolygonBuffer
All you need to use is this line
double distance = 0.0001;
List bufferedPolygonList = AreaBuffer.buffer(pointList, distance);
It gives you a list of buffered polygon points at a given distance from your original polygon.
I would recommend to use Turf.js library for buffering and many basic gis operations. You would be able to retrieve each edge from the path that returned. For geometry buffer, it is easy to use, quite light weighted and it works without any problem for my applications using MapBox.js or leaflet.
More details : Turf.js Buffer
But if you are looking for a geodesic distance buffer that could be problem. I would use Arcgis Javascript API
Take a look at BOOST this is a big C++ library, you may find library/source code for almost everything up there like, buffer methods with different types such as miter,round,square.
Just install the latest version of the Boost which I guess is 1.58.0 right now, and take a look at BOOST/Geometry/Strategies/Cartesian/buffer[Something]-Square/Miter/Round
Here it is a good document
You need to convert your geodetic coordinates (lat/long) to cartesian (x/y) and use the Boost library and reverse the conversion. you do not need to use ArcGIS or any other GIS library at all.

Create perpendicular lat long from single cllocation coordinate of X meter

I have user current location i.e. CLLocation Coordinate (location lat & long) and user is on race track pointing to one direction with the help of user current location i created one region now I want some more race track coordinate(say 2m , 4m , 6m away from race track in perpendicular direction) and the track is 10 m long. Please check the image and the red points are on the track.
Please check this image
/**
* Returns the destination point from initial point having travelled the given distance on the
* given initial bearing (bearing normally varies around path followed).
*
* #param {double} distance - Distance travelled, in same units as earth radius (default: metres).
* #param {double} bearing - Initial bearing in degrees from north.
*
* #returns {CLLocationCoordinate} Destination point.
*/
#define kEarthRadius 6378137
- (CLLocationCoordinate2D)destinationPointWithStartingPoint:(MKMapPoint)initialPoint distance:(double)distance andBearing:(double)bearing {
CLLocationCoordinate2D location = MKCoordinateForMapPoint(initialPoint);
double delta = distance / kEarthRadius;
double omega = [self degreesToRadians:bearing];
double phi1 = [self degreesToRadians:location.latitude];
double lambda1 = [self degreesToRadians:location.longitude];
double phi2 = asin(sin(phi1)*cos(delta) + cos(phi1) * sin(delta) * cos(omega));
double x = cos(delta) - sin(phi1) * sin(phi2);
double y = sin(omega) * sin(delta) * cos(phi1);
double lambda2 = lambda1 + atan2(y, x);
return CLLocationCoordinate2DMake([self radiansToDegrees:phi2], ([self radiansToDegrees:lambda2]+540)%360-180);
}
- (CLLocationCoordinate2D)rhumbDestinationPointForInitialPoint:(MKMapPoint)initialPoint distance:(double)distance andBearing:(double)bearing {
CLLocationCoordinate2D location = MKCoordinateForMapPoint(initialPoint);
double delta = distance / kEarthRadius;
double omega = [self degreesToRadians:bearing];
double phi1 = [self degreesToRadians:location.latitude];
double lambda1 = [self degreesToRadians:location.longitude];
double delta_phi = delta * cos(omega);
double phi2 = phi1 + delta_phi;
// check for some daft bugger going past the pole, normalise latitude if so
if (fabs(phi2) > M_PI / 2) {
phi2 = phi2 > 0 ? M_PI-phi2 : -M_PI-phi2;
}
double delta_gamma = log(tan(phi2/2+M_PI/4)/tan(phi1/2+M_PI/4));
double q = fabs(delta_gamma) > 10e-12 ? delta_phi / delta_gamma : cos(phi1);
double delta_lambda = delta*sin(omega)/q;
double lambda2 = lambda1 + delta_lambda;
return CLLocationCoordinate2DMake([self radiansToDegrees:phi2], ([self radiansToDegrees:lambda2]+540)%360-180);
}
- (double)degreesToRadians:(double)degrees {
return degrees * M_PI / 180.0;
}
- (double)radiansToDegrees:(double)radians {
return radians * 180.0 / M_PI;
}
Adapted from : http://www.movable-type.co.uk/scripts/latlong.html
More information on bearing : https://en.wikipedia.org/wiki/Bearing_(navigation)
And rhumb line : https://en.wikipedia.org/wiki/Rhumb_line

Can't get Panoramio Data with API

I am trying to get panoramio picture around a given coordinate. However always my query returns zero photos. This is the code I am using.
const double WGS84_a = 6378137.0;
const double WGS84_b = 6356752.3;
double Deg2rad(double degrees) {
return degrees * M_PI / 180.0;
}
double Rad2deg(double radians) {
return radians * 180.0 / M_PI;
}
double WGS84EarthRadius(double lat)
{
double An = WGS84_a*WGS84_a * cos(lat);
double Bn = WGS84_b*WGS84_b * sin(lat);
double Ad = WGS84_a * cos(lat);
double Bd = WGS84_b * sin(lat);
return sqrt( (An*An + Bn*Bn)/(Ad*Ad + Bd*Bd) );
}
MapRect LatLonDestPoint(CLLocationCoordinate2D origin, double halfSideInKm) {
double lat = Deg2rad(origin.latitude);
double lon = Deg2rad(origin.longitude);
double halfSide = 1000*halfSideInKm;
double radius = WGS84EarthRadius(lat);
double pradius = radius*cos(lat);
double latMin = lat - halfSide/radius;
double latMax = lat + halfSide/radius;
double lonMin = lon - halfSide/pradius;
double lonMax = lon + halfSide/pradius;
return MKMapRectMake(Rad2deg(latMin), Rad2deg(lonMin), Rad2deg(latMax), Rad2deg(lonMax));
}
Now for a coordinate (60.1190935704,-149.439081366) I get the API like
http://www.panoramio.com/map/get_panoramas.php?set=public&from=0&to=20&minx=60.029034&miny=-149.619843&maxx=60.209152&maxy=-149.258316&size=medium&mapfilter=true
This always returns me zero results. Please help me with what I am doing wrong.
You have x and y coordinates the wrong way around.
Swapping these returns:-
{"count":379,"has_more":true,"map_location":{"lat":60.118290103595498,"lon":-149.45469385385852,"panoramio_zoom":6}
etc
ps Do not rely on the count being correct. Use the has_more flag.

Radar like LOVOO app with slider

I am working on an app which has a functionality of RADAR just like LOVOO app. I don't have experience of working on CoreLocation and other location based frameworks.
It would be much appreciated if you could suggest me how should this can be achieved.
What frameworks should i use and how to proceed initially.
Though same question already exists on SO over here my question is same as Radar View like LOVOO but its of no use to me thats why i am asking it again.
What i have tried myself so far is, i have lat and long values of points to plot and i have calculated angle and distance between centre point(my location) and other point
- (float)angletoCoordinate:(CLLocationCoordinate2D)second {
//myCurrentLocation is origin
//second is point
float lat1 = DegreesToRadians(myCurrentLocation.coordinate.latitude);
float lon1 = DegreesToRadians(myCurrentLocation.coordinate.longitude);
float lat2 = DegreesToRadians(second.latitude);
float lon2 = DegreesToRadians(second.longitude);
float dLon = lon2 - lon1;
float y = sin(dLon) * cos(lat2);
float x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
float radiansBearing = atan2(y, x);
if(radiansBearing < 0.0)
{
radiansBearing += 2*M_PI;
}
return radiansBearing;
}
-(float)calculateXPointWithLoc:(ARGeoLocation *)loc andDelta:(float)delta{
float angle = radiansToDegrees(delta);
float dpx = (([myCurrentLocation distanceFromLocation:loc.geoLocation])/1000);
if(0<=angle<=90)
return viewRadar.center.x + sin(angle)*dpx ;
else if(90<angle<=180)
return viewRadar.center.x + cos(angle-90)*dpx ;
else if(180<angle<=270)
return viewRadar.center.x - cos(270-angle)*dpx ;
else if(270<angle<360)
return viewRadar.center.x - sin(360-angle)*dpx ;
return 0;
}
-(float)calculateYPointWithLoc:(ARGeoLocation *)loc andDelta:(float)delta{
float angle = radiansToDegrees(delta);
float dpx = (([myCurrentLocation distanceFromLocation:loc.geoLocation])/1000);
if(0<=angle<=90)
return viewRadar.center.y - cos(angle)*dpx ;
else if(90<angle<=180)
return viewRadar.center.y + sin(angle-90)*dpx ;
else if(180<angle<=270)
return viewRadar.center.y + sin(270-angle)*dpx ;
else if(270<angle<360)
return viewRadar.center.y - cos(360-angle)*dpx ;
return 0;
}
and then
int i = 0;
for(ARGeoLocation *loc in coordinates){
deltaAz = [self angletoCoordinate:loc.geoLocation.coordinate];
x = [self calculateXPointWithLoc:loc andDelta:deltaAz];
y = [self calculateYPointWithLoc:loc andDelta:deltaAz];
[[plots objectAtIndex:i] setFrame:CGRectMake(x, y, DIAMETER_PLOT, DIAMETER_PLOT)];
i++;
}
I am not sure whether x and y are correct or not also if they are correct then how can i change these value with change of slider value.
I think the keyword here is Geofencing.
Geofencing is the automatic triggering of an action if your device enters or leaves a certain region. For your case your action is to display the profiles of those users who enter the area of your radar.
Basically you need to calculate a circular region (given a radius) and display all other points within your region.
I once found this tutorial which could teach how to do it by yourself:
http://www.raywenderlich.com/95014/geofencing-ios-swift
I hope it helps!

How to find the angular direction to a point in the map (respect to north)

I need to find the angle between the north and a point in a map (21.4167, -39.8167).
here is my code
double MECCA_LONGITUDE = 21.4167;
double MECCA_LATITUDE = -39.8167;
-(void) getAngle :(float) phoneAngle: (float) lon:(float) lat
{
float ad = atan (sin (lon - MECCA_LATITUDE) / ( (cos (lat)) * (tan (MECCA_LATITUDE)) ) - ( (sin (lat)) * (cos (lon - MECCA_LONGITUDE)) ));
NSLog(#"###### %f", CC_RADIANS_TO_DEGREES(ad));
}
I'm still not getting the correct answer. Any tips would be appreciated. Thanks.
Your principle problem is that sin() and cos() take their arguments in radians, and you are passing degrees (you also have the longitude and latitude of Mecca interchanged).
Try:
double MECCA_LATITUDE = 21.4167;
double MECCA_LONGITUDE = 39.8167;
double r_delta_lon = CC_DEGREES_TO_RADIANS(MECCA_LONGITUDE - lon);
double r_lat2 = CC_DEGREES_TO_RADIANS(MECCA_LATITUDE);
double r_lon1 = CC_DEGREES_TO_RADIANS(lon);
double r_lat1 = CC_DEGREES_TO_RADIANS(lat);
double ad = atan2(sin(r_delta_lon) * cos(r_lat2),
cos(r_lat1) * sin(r_lat2) - sin(r_lat1) * cos(r_lat2) * cos(r_delta_lon));

Resources