How calulate the area of a polygon in react-native-maps? - react-native

I am passing the array of co-ordinates to the polygon and want to find the area of the polygon through that co-ordinates. I have checked the documentation of the react-native-maps but there is no function provided.
Is there is any way to calculate the area.
Thanks in advance.

Library will not give you this functionality.
Try this
function calcArea(locations) {
if (!locations.length) {
return 0;
}
if (locations.length < 3) {
return 0;
}
let radius = 6371000;
const diameter = radius * 2;
const circumference = diameter * Math.PI;
const listY = [];
const listX = [];
const listArea = [];
// calculate segment x and y in degrees for each point
const latitudeRef = locations[0].latitude;
const longitudeRef = locations[0].longitude;
for (let i = 1; i < locations.length; i++) {
let latitude = locations[i].latitude;
let longitude = locations[i].longitude;
listY.push(this.calculateYSegment(latitudeRef, latitude, circumference));
listX.push(this.calculateXSegment(longitudeRef, longitude, latitude, circumference));
}
// calculate areas for each triangle segment
for (let i = 1; i < listX.length; i++) {
let x1 = listX[i - 1];
let y1 = listY[i - 1];
let x2 = listX[i];
let y2 = listY[i];
listArea.push(this.calculateAreaInSquareMeters(x1, x2, y1, y2));
}
// sum areas of all triangle segments
let areasSum = 0;
listArea.forEach(area => areasSum = areasSum + area)
// get abolute value of area, it can't be negative
let areaCalc = Math.abs(areasSum);// Math.sqrt(areasSum * areasSum);
return areaCalc;
}
function calculateAreaInSquareMeters(x1, x2, y1, y2) {
return (y1 * x2 - x1 * y2) / 2;
}
function calculateYSegment(latitudeRef, latitude, circumference) {
return (latitude - latitudeRef) * circumference / 360.0;
}
function calculateXSegment(longitudeRef, longitude, latitude, circumference) {
return (longitude - longitudeRef) * circumference * Math.cos((latitude * (Math.PI / 180))) / 360.0;
}
Reference

Related

React native SVG building pie chart with Path

I have a code that builds a SVG figure for a pie chart, using this function:
export const generateArc = (percentage: number, radius: number) => {
const a = (percentage * 2 * Math.PI) / 100 // angle (in radian) depends on percentage
const r = radius // radius of the circle
const rx = r
const ry = r
const xAxisRotation = 0
let largeArcFlag = 1
const sweepFlag = 1
const x = r + r * Math.sin(a)
const y = r - r * Math.cos(a)
if (percentage <= 50) {
largeArcFlag = 0
} else {
largeArcFlag = 1
}
return `A${rx} ${ry} ${xAxisRotation} ${largeArcFlag} ${sweepFlag} ${x} ${y}`
}
Everything works fine, except when percentage is 100, the chart disappears, you can look here
I feel like that there's some miscalculation, but i don't have much experience in such things with SVGS, and i can't figure it out.
The workarounds I've found are:
iOS: check if percentage is 100, then use 99.9, but that doesn't work for android (i've tested on real device, there's a little gap that's not filled, but on some emulators 99.9999 gives the full circle), you can check it in a snack
For both platform just use a full circle SVG instead, if percentage is 100, but i don't like that workaround
Thanks to #Robert Longson I've modified the function that generates arc a bit, so now it generates 2 arcs if percentage is 100
const generatePath = (percentage: number, radius: number) => {
if (percentage === 100) {
const x = radius
const y = radius
// in path x has to be 0 = starting point
return `M ${0}, ${y}
a ${x},${y} 0 1,1 ${radius * 2},0
a ${x},${y} 0 1,1 -${radius * 2},0
`
}
const a = (percentage * 2 * Math.PI) / 100 // angle (in radian) depends on percentage
const r = radius // radius of the circle
const rx = r
const ry = r
const xAxisRotation = 0
let largeArcFlag = 1
const sweepFlag = 1
const x = r + r * Math.sin(a)
const y = r - r * Math.cos(a)
if (percentage <= 50) {
largeArcFlag = 0
} else {
largeArcFlag = 1
}
return `M${radius} ${radius} L${radius} 0 A${rx} ${ry} ${xAxisRotation} ${largeArcFlag} ${sweepFlag} ${x} ${y} Z`
}

Calculate vertical bearing between two GPS coordinates with altitudes

I am planning to build an antenna tracker. I need to get bearing and tilt from GPS point A with altitude and GPS point B with altitude.
This is the example points:
latA = 39.099912
lonA = -94.581213
altA = 273.543
latB = 38.627089
lonB = -90.200203
altB = 1380.245
I've already got the formula for horizontal bearing and it gives me 97.89138167122422
This is the code:
function toRadian(num) {
return num * (Math.PI / 180);
}
function toDegree(num) {
return num * (180 / Math.PI);
}
function getHorizontalBearing(fromLat, fromLon, toLat, toLon) {
fromLat = toRadian(fromLat);
fromLon = toRadian(fromLon);
toLat = toRadian(toLat);
toLon = toRadian(toLon);
let dLon = toLon - fromLon;
let x = Math.tan(toLat / 2 + Math.PI / 4);
let y = Math.tan(fromLat / 2 + Math.PI / 4);
let dPhi = Math.log(x / y);
if (Math.abs(dLon) > Math.PI) {
if (dLon > 0.0) {
dLon = -(2 * Math.PI - dLon);
} else {
dLon = (2 * Math.PI + dLon);
}
}
return (toDegree(Math.atan2(dLon, dPhi)) + 360) % 360;
}
let n = getHorizontalBearing(39.099912, -94.581213, 38.627089, -90.200203);
console.info(n);
But I don't know how to find the tilt angle. Anyone could help me?
I think I got the answer after searching around.
This is the complete code, if you think this is wrong, feel free to correct me.
function toRadian(num) {
return num * (Math.PI / 180);
}
function toDegree(num) {
return num * (180 / Math.PI);
}
// North is 0 degree, South is 180 degree
function getHorizontalBearing(fromLat, fromLon, toLat, toLon, currentBearing) {
fromLat = toRadian(fromLat);
fromLon = toRadian(fromLon);
toLat = toRadian(toLat);
toLon = toRadian(toLon);
let dLon = toLon - fromLon;
let x = Math.tan(toLat / 2 + Math.PI / 4);
let y = Math.tan(fromLat / 2 + Math.PI / 4);
let dPhi = Math.log(x / y);
if (Math.abs(dLon) > Math.PI) {
if (dLon > 0.0) {
dLon = -(2 * Math.PI - dLon);
} else {
dLon = (2 * Math.PI + dLon);
}
}
let targetBearing = (toDegree(Math.atan2(dLon, dPhi)) + 360) % 360;
return targetBearing - currentBearing;
}
// Horizon is 0 degree, Up is 90 degree
function getVerticalBearing(fromLat, fromLon, fromAlt, toLat, toLon, toAlt, currentElevation) {
fromLat = toRadian(fromLat);
fromLon = toRadian(fromLon);
toLat = toRadian(toLat);
toLon = toRadian(toLon);
let fromECEF = getECEF(fromLat, fromLon, fromAlt);
let toECEF = getECEF(toLat, toLon, toAlt);
let deltaECEF = getDeltaECEF(fromECEF, toECEF);
let d = (fromECEF[0] * deltaECEF[0] + fromECEF[1] * deltaECEF[1] + fromECEF[2] * deltaECEF[2]);
let a = ((fromECEF[0] * fromECEF[0]) + (fromECEF[1] * fromECEF[1]) + (fromECEF[2] * fromECEF[2]));
let b = ((deltaECEF[0] * deltaECEF[0]) + (deltaECEF[2] * deltaECEF[2]) + (deltaECEF[2] * deltaECEF[2]));
let elevation = toDegree(Math.acos(d / Math.sqrt(a * b)));
elevation = 90 - elevation;
return elevation - currentElevation;
}
function getDeltaECEF(from, to) {
let X = to[0] - from[0];
let Y = to[1] - from[1];
let Z = to[2] - from[2];
return [X, Y, Z];
}
function getECEF(lat, lon, alt) {
let radius = 6378137;
let flatteningDenom = 298.257223563;
let flattening = 0.003352811;
let polarRadius = 6356752.312106893;
let asqr = radius * radius;
let bsqr = polarRadius * polarRadius;
let e = Math.sqrt((asqr-bsqr)/asqr);
// let eprime = Math.sqrt((asqr-bsqr)/bsqr);
let N = getN(radius, e, lat);
let ratio = (bsqr / asqr);
let X = (N + alt) * Math.cos(lat) * Math.cos(lon);
let Y = (N + alt) * Math.cos(lat) * Math.sin(lon);
let Z = (ratio * N + alt) * Math.sin(lat);
return [X, Y, Z];
}
function getN(a, e, latitude) {
let sinlatitude = Math.sin(latitude);
let denom = Math.sqrt(1 - e * e * sinlatitude * sinlatitude);
return a / denom;
}
let n = getHorizontalBearing(39.099912, -94.581213, 39.099912, -94.588032, 0.00);
console.info("Horizontal bearing:\t", n);
let m = getVerticalBearing(39.099912, -94.581213, 273.543, 39.099912, -94.588032, 873.543, 0.0);
console.info("Vertical bearing:\t", m);
Don Cross's javascript code produces good results. It takes into consideration the curvature of the earth plus the fact that the earth is oblate.
Example:
var elDegrees = calculateElevationAngleCosineKitty(
{latitude: 35.346257, longitude: -97.863801, altitudeMetres: 10},
{latitude: 34.450545, longitude: -96.500167, altitudeMetres: 9873}
);
console.log("El: " + elDegrees);
/***********************************
Code by Don Cross at cosinekitty.com
http://cosinekitty.com/compass.html
************************************/
function calculateElevationAngleCosineKitty(source, target)
{
var oblate = true;
var a = {'lat':source.latitude, 'lon':source.longitude, 'elv':source.altitudeMetres};
var b = {'lat':target.latitude, 'lon':target.longitude, 'elv':target.altitudeMetres};
var ap = LocationToPoint(a, oblate);
var bp = LocationToPoint(b, oblate);
var bma = NormalizeVectorDiff(bp, ap);
var elevation = 90.0 - (180.0 / Math.PI)*Math.acos(bma.x*ap.nx + bma.y*ap.ny + bma.z*ap.nz);
return elevation;
}
function NormalizeVectorDiff(b, a)
{
// Calculate norm(b-a), where norm divides a vector by its length to produce a unit vector.
var dx = b.x - a.x;
var dy = b.y - a.y;
var dz = b.z - a.z;
var dist2 = dx*dx + dy*dy + dz*dz;
if (dist2 == 0) {
return null;
}
var dist = Math.sqrt(dist2);
return { 'x':(dx/dist), 'y':(dy/dist), 'z':(dz/dist), 'radius':1.0 };
}
function EarthRadiusInMeters (latitudeRadians) // latitude is geodetic, i.e. that reported by GPS
{
// http://en.wikipedia.org/wiki/Earth_radius
var a = 6378137.0; // equatorial radius in meters
var b = 6356752.3; // polar radius in meters
var cos = Math.cos (latitudeRadians);
var sin = Math.sin (latitudeRadians);
var t1 = a * a * cos;
var t2 = b * b * sin;
var t3 = a * cos;
var t4 = b * sin;
return Math.sqrt ((t1*t1 + t2*t2) / (t3*t3 + t4*t4));
}
function GeocentricLatitude(lat)
{
// Convert geodetic latitude 'lat' to a geocentric latitude 'clat'.
// Geodetic latitude is the latitude as given by GPS.
// Geocentric latitude is the angle measured from center of Earth between a point and the equator.
// https://en.wikipedia.org/wiki/Latitude#Geocentric_latitude
var e2 = 0.00669437999014;
var clat = Math.atan((1.0 - e2) * Math.tan(lat));
return clat;
}
function LocationToPoint(c, oblate)
{
// Convert (lat, lon, elv) to (x, y, z).
var lat = c.lat * Math.PI / 180.0;
var lon = c.lon * Math.PI / 180.0;
var radius = oblate ? EarthRadiusInMeters(lat) : 6371009;
var clat = oblate ? GeocentricLatitude(lat) : lat;
var cosLon = Math.cos(lon);
var sinLon = Math.sin(lon);
var cosLat = Math.cos(clat);
var sinLat = Math.sin(clat);
var x = radius * cosLon * cosLat;
var y = radius * sinLon * cosLat;
var z = radius * sinLat;
// We used geocentric latitude to calculate (x,y,z) on the Earth's ellipsoid.
// Now we use geodetic latitude to calculate normal vector from the surface, to correct for elevation.
var cosGlat = Math.cos(lat);
var sinGlat = Math.sin(lat);
var nx = cosGlat * cosLon;
var ny = cosGlat * sinLon;
var nz = sinGlat;
x += c.elv * nx;
y += c.elv * ny;
z += c.elv * nz;
return {'x':x, 'y':y, 'z':z, 'radius':radius, 'nx':nx, 'ny':ny, 'nz':nz};
}
/***********************
END cosinekitty.com code
************************/

Bezier path see if it crosses

I have a code that lets the user draw a shape, I'm using UIBezierPath for this. But I need to see if the shape crosses itself, for example like this: http://upload.wikimedia.org/wikipedia/commons/0/0f/Complex_polygon.svg
Then it's not a a valid shape.
How can I find this?
Edit:
I still haven't solved this. I save all the points between the lines in the path in a array. And then I loop through the array and try to find if any lines intersects. But it does not work, sometimes it says that there is an intersection when it isn't.
I think that the problem is somewhere in this method.
-(BOOL)pathIntersects:(double *)x:(double *)y {
int count = pathPoints.count;
CGPoint p1, p2, p3, p4;
for (int a=0; a<count; a++) {
//Line 1
if (a+1<count) {
p1 = [[pathPoints objectAtIndex:a] CGPointValue];
p2 = [[pathPoints objectAtIndex:a+1] CGPointValue];
}else{
return NO;
}
for (int b=0; b<count; b++) {
//Line 2
if (b+1<count) {
p3 = [[pathPoints objectAtIndex:b] CGPointValue];
p4 = [[pathPoints objectAtIndex:b+1] CGPointValue];
}else{
return NO;
}
if (!CGPointEqualToPoint(p1, p3) && !CGPointEqualToPoint(p2, p3) && !CGPointEqualToPoint(p4, p1) && !CGPointEqualToPoint(p4, p2)
&& !CGPointEqualToPoint(p1, p2) && !CGPointEqualToPoint(p3, p4)) {
if (LineIntersect(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, x, y)) {
return YES;
}
}
}
}
return NO;
}
This is the code I found to see if two lines intersects, It's in C but I should work.
int LineIntersect(
double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4,
double *x, double *y)
{
double mua,mub;
double denom,numera,numerb;
denom = (y4-y3) * (x2-x1) - (x4-x3) * (y2-y1);
numera = (x4-x3) * (y1-y3) - (y4-y3) * (x1-x3);
numerb = (x2-x1) * (y1-y3) - (y2-y1) * (x1-x3);
/* Are the line coincident? */
if (ABS(numera) < 0.00001 && ABS(numerb) < 0.00001 && ABS(denom) < 0.00001) {
*x = (x1 + x2) / 2;
*y = (y1 + y2) / 2;
return(TRUE);
}
/* Are the line parallel */
if (ABS(denom) < 0.00001) {
*x = 0;
*y = 0;
return(FALSE);
}
/* Is the intersection along the the segments */
mua = numera / denom;
mub = numerb / denom;
if (mua < 0 || mua > 1 || mub < 0 || mub > 1) {
*x = 0;
*y = 0;
return(FALSE);
}
*x = x1 + mua * (x2 - x1);
*y = y1 + mua * (y2 - y1);
return(TRUE);
}
It depends on how complex the polygon drawn by the user can be and the number of points in the path. Ideally, there would be a point for all the vertices in the shape and nothing more. Get a CGPath from the UIBezierPath and use GCPathApply to hand the elements to a function, which adds each point to an array. Traverse the array with two for loops, one nested in the other, which checks each line segment against every line segment after it using a standard line-line intersection test. As soon as an intersection has been found, break from the loop. Or, if this were a convenience method, return a BOOL. That's the simplest way.
EDIT: Here's an example of a line-line intersection function which returns a BOOL telling you whether or not two segments cross. Pass in the two points that create the first segment followed by the two points that make the second segment. It was hastily modified from a piece of source code I found online quickly, but it works.
CGPoint lineSegmentsIntersect(CGPoint L1P1, CGPoint L1P2, CGPoint L2P1, CGPoint L2P2)
{
float x1 = L1P1.x, x2 = L1P2.x, x3 = L2P1.x, x4 = L2P2.x;
float y1 = L1P1.y, y2 = L1P2.y, y3 = L2P1.y, y4 = L2P2.y;
float bx = x2 - x1;
float by = y2 - y1;
float dx = x4 - x3;
float dy = y4 - y3;
float b_dot_d_perp = bx * dy - by * dx;
if(b_dot_d_perp == 0) {
return NO;
}
float cx = x3 - x1;
float cy = y3 - y1;
float t = (cx * dy - cy * dx) / b_dot_d_perp;
if(t < 0 || t > 1) {
return NO;
}
float u = (cx * by - cy * bx) / b_dot_d_perp;
if(u < 0 || u > 1) {
return NO;
}
return YES;
}
You can use it like this.
if (lineSegmentsIntersect(lineOnePointOne,lineOnePointTwo,lineTwoPointOne,lineTwoPointTwo)){
//segments intersect
} else {
//segments did not intersect
}
It's up to you to create the double loop to check the correct segments against one another.

Determining Midpoint Between 2 Coordinates

I am trying to determine the midpoint between two locations in an MKMapView. I am following the method outlined here (and here) and rewrote it in Objective-C, but the map is being centered somewhere northeast of Baffin Island, which is no where near the two points.
My method based on the java method linked above:
+(CLLocationCoordinate2D)findCenterPoint:(CLLocationCoordinate2D)_lo1 :(CLLocationCoordinate2D)_loc2 {
CLLocationCoordinate2D center;
double lon1 = _lo1.longitude * M_PI / 180;
double lon2 = _loc2.longitude * M_PI / 100;
double lat1 = _lo1.latitude * M_PI / 180;
double lat2 = _loc2.latitude * M_PI / 100;
double dLon = lon2 - lon1;
double x = cos(lat2) * cos(dLon);
double y = cos(lat2) * sin(dLon);
double lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) );
double lon3 = lon1 + atan2(y, cos(lat1) + x);
center.latitude = lat3 * 180 / M_PI;
center.longitude = lon3 * 180 / M_PI;
return center;
}
The 2 parameters have the following data:
_loc1:
latitude = 45.4959839
longitude = -73.67826455
_loc2:
latitude = 45.482889
longitude = -73.57522299
The above are correctly place on the map (in and around Montreal). I am trying to center the map in the midpoint between the 2, yet my method return the following:
latitude = 65.29055
longitude = -82.55425
which somewhere in the arctic, when it should be around 500 miles south.
In case someone need code in Swift, I have written library function in Swift to calculate the midpoint between MULTIPLE coordinates:
// /** Degrees to Radian **/
class func degreeToRadian(angle:CLLocationDegrees) -> CGFloat {
return ( (CGFloat(angle)) / 180.0 * CGFloat(M_PI) )
}
// /** Radians to Degrees **/
class func radianToDegree(radian:CGFloat) -> CLLocationDegrees {
return CLLocationDegrees( radian * CGFloat(180.0 / M_PI) )
}
class func middlePointOfListMarkers(listCoords: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
var x = 0.0 as CGFloat
var y = 0.0 as CGFloat
var z = 0.0 as CGFloat
for coordinate in listCoords{
var lat:CGFloat = degreeToRadian(coordinate.latitude)
var lon:CGFloat = degreeToRadian(coordinate.longitude)
x = x + cos(lat) * cos(lon)
y = y + cos(lat) * sin(lon)
z = z + sin(lat)
}
x = x/CGFloat(listCoords.count)
y = y/CGFloat(listCoords.count)
z = z/CGFloat(listCoords.count)
var resultLon: CGFloat = atan2(y, x)
var resultHyp: CGFloat = sqrt(x*x+y*y)
var resultLat:CGFloat = atan2(z, resultHyp)
var newLat = radianToDegree(resultLat)
var newLon = radianToDegree(resultLon)
var result:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: newLat, longitude: newLon)
return result
}
Detailed answer can be found here
Updated For Swift 5
func geographicMidpoint(betweenCoordinates coordinates: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
guard coordinates.count > 1 else {
return coordinates.first ?? // return the only coordinate
CLLocationCoordinate2D(latitude: 0, longitude: 0) // return null island if no coordinates were given
}
var x = Double(0)
var y = Double(0)
var z = Double(0)
for coordinate in coordinates {
let lat = coordinate.latitude.toRadians()
let lon = coordinate.longitude.toRadians()
x += cos(lat) * cos(lon)
y += cos(lat) * sin(lon)
z += sin(lat)
}
x /= Double(coordinates.count)
y /= Double(coordinates.count)
z /= Double(coordinates.count)
let lon = atan2(y, x)
let hyp = sqrt(x * x + y * y)
let lat = atan2(z, hyp)
return CLLocationCoordinate2D(latitude: lat.toDegrees(), longitude: lon.toDegrees())
}
}
Just a hunch, but I noticed your lon2 and lat2 variables are being computed with M_PI/100 and not M_PI/180.
double lon1 = _lo1.longitude * M_PI / 180;
double lon2 = _loc2.longitude * M_PI / 100;
double lat1 = _lo1.latitude * M_PI / 180;
double lat2 = _loc2.latitude * M_PI / 100;
Changing those to 180 might help you out a bit.
For swift users, corrected variant as #dinjas suggest
import Foundation
import MapKit
extension CLLocationCoordinate2D {
// MARK: CLLocationCoordinate2D+MidPoint
func middleLocationWith(location:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
let lon1 = longitude * M_PI / 180
let lon2 = location.longitude * M_PI / 180
let lat1 = latitude * M_PI / 180
let lat2 = location.latitude * M_PI / 180
let dLon = lon2 - lon1
let x = cos(lat2) * cos(dLon)
let y = cos(lat2) * sin(dLon)
let lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) )
let lon3 = lon1 + atan2(y, cos(lat1) + x)
let center:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat3 * 180 / M_PI, lon3 * 180 / M_PI)
return center
}
}
It's important to say that the formula the OP used to calculate geographic midpoint is based on this formula which explains the cos/sin/sqrt calculation.
This formula will give you the geographic midpoint for any long distance including the four quarters and the prime meridian.
But, if your calculation is for short-range around 1 Kilometer, using a simple average will produce the same midpoint results.
i.e:
let firstPoint = CLLocation(....)
let secondPoint = CLLocation(....)
let midPointLat = (firstPoint.coordinate.latitude + secondPoint.coordinate.latitude) / 2
let midPointLong = (firstPoint.coordinate.longitude + secondPoint.coordinate.longitude) / 2
You can actually use it for 10km but expect a deviation - if you only need an estimation for a short range midpoint with a fast solution it will be sufficient.
I think you are over thinking it a bit. Just do:
float lon3 = ((lon1 + lon2) / 2)
float lat3 = ((lat1 + lat2) / 2)
lat3 and lon3 will be the center point.

How to find a third point using two other points and their angle

I found an answer here, but can't understand how to transfer the math to Objective C
Find the third point
I have two points and I also have the angle relative to the axes. How do I find a third point which will form a straight line? The distance should be variable.
This is the code that I am using:
float distanceFromPx2toP3 = 1300.0;
float mag = sqrt(pow((px2.x - px1.x),2) + pow((px2.y - px1.y),2));
float P3x = px2.x + distanceFromPx2toP3 * (px2.x - px1.x) / mag;
float P3y = px2.y + distanceFromPx2toP3 * (px2.y - px1.y) / mag;
CGPoint P3 = CGPointMake(P3x, P3y);
Let's say I have two points pointA and pointB. The slope of the line formed by the two points m is:
static CGFloat calculateSlope(CGPoint pointA, CGPoint pointB) {
CGFloat m = (pointB.y - pointA.y) / (pointB.x - pointA.x);
return m;
}
A third point pointC a distance d from pointA on the line would be given by:
static CGPoint calculatePointOnLine(
CGPoint pointA, CGPoint pointB, CGFloat d, BOOL startAtB) {
CGFloat m = calculateSlope(pointA, pointB);
CGFloat dX = pointB.x - pointA.x;
CGFloat dY = pointB.y - pointA.y;
CGFloat signDX = dX / fabsf(dX);
CGFloat signDY = dY / fabsf(dY);
CGFloat dSquared = d * d;
CGFloat mSquared = m * m;
// We know pointC is distance d from pointA,
// and that pointA and pointC are on the
// same line
// dXSquared + dYSquared = dSquared
// m = dY / dX
// dY = m * dX
// dXSquared + mSquared * dXSquared = dSquared
// dXSquared * ( 1 + mSquared ) = dSquared
// dXSquared = dSquared / ( 1 + mSquared )
// Handle a vertical line, dX == 0, and a horizontal line, dY == 0
if (dX != 0 && dY != 0) {
// Account for the sign of dX
dX = signDX * sqrtf(dSquared / ( 1 + mSquared ));
// Account for the sign of dY
dY = signDY * m * dX;
}
// Handle a vertical line, dX == 0
if (dX == 0 && dY != 0) {
dY = signDY * d;
}
// Handle a horizontal line, dY == 0
if (dY == 0 && dX != 0) {
dX = signDX * d;
}
CGPoint startingPoint = pointA;
if (startAtB) {
startingPoint = pointB;
}
CGPoint pointC = CGMakePoint(startingPoint.x + dX,
startingPoint.y + dY);
return pointC;
}
pointC will now always lie a distance d along the line from pointA,
in the direction from pointA to pointB. Pass startAtB to have pointC
lie a distance d along the line from pointB, in the direction from
pointA to pointB.
Exchange the order of piintA and pointB in the call to calculatPointOnLine
to calculate a pointC which lies a distance d along the line from
PointB, in the direction from pointB to pointA.
You can use these two functions to calculate a third point on the line.
Thanks for accepting this answer if this helps you.