Getting coordinates perpendicular to AB - javascript

I already posted this question here
And I got the solution thanks to Fang but the problem is that the coordinates I need to use are GPS coordinates and apparently GPS needs a different formula than cartesian.
So I am working with Google Maps APIv3 polygons and having coordinates of AB (by drawing them with the polygon tool) I click on position C which I need to be moved to D which is perpendicular to AB and CD is parallel to AB
So the question would be:
Having
A = 50.88269282423443,6.0036662220954895
B = 50.882753744583226,6.003803014755249
C = 50.88252571592428, 6.003832183778286
- D is perpendicular to AB
- CD is parallel to AB
What would be the formula to get D
I've been trying a long time to figure it out but no succes so far.

so A,B,C are knowns 'D' is unknown.
axis CD
is cd(t1)=C+(B-A)*t1
where:
cd(t1) is any point on CD
t1 is parameter from interval <-inf,+inf>
axis BD
is bd(t2)=B+Q*t2
where:
bd(t2) is any point on BD
t2 is parameter from interval <-inf,+inf>
Q is vector perpendicular to B-A
in 2D you can obtain it like this:
Q.x=+(B-A).y
Q.y=-(B-A).x
in 3D use cross product but your example implies 2D case...
point D
is intersection of BD and CD
so just solve algebraically this:
I. cd(t1)=C+(B-A)*t1
II. bd(t2)=B+Q*t2
III. cd(t1)=bd(t2)
in 2D this (III.) leads to 2 linear equations with 2 variables ...
C.x+(B.x-A.x)*t1=B.x+(B.y-A.y)*t2
C.y+(B.y-A.y)*t1=B.y-(B.x-A.x)*t2
find the parameter t1 and then compute the D=cd(t1)
or t2 and then compute the D=bd(t2)
you should derivate both solutions and use one with better precision
both will use some division A1/A2 so chose one with bigger|A2|
if your point D can lock also to the point A
then find both positions D1 locked to A and D2 locked to B
and chose one that is closer to the point C (min(|D1-C|,|D2-C|))
to simplify this you can use D1+(B-A)=D2 ...
[edit2] I did make some mistake somewhere in edit1 so here is working version
double ax,ay,bx,by,cx,cy,dx,dy; // points
double bdx,bdy,cdx,cdy; // directions
double t1,t2; // parameters
/*
//--- intersection equations ----------------
1. cx+cdx*t1=bx+bdx*t2;
2. cy+cdy*t1=by+bdy*t2;
//--- separate t1 ---------------------------
1. t1=(bx-cx+(bdx*t2)/cdx;
//--- substitute t1 and separate t2 ---------
2. t2=(cy-by+((bx-cx)*cdy/cdx))/(bdy-(bdx*cdy/cdx));
//-------------------------------------------
//--- separate t2 ---------------------------
1. t2=(cx-bx+cdx*t1)/bdx;
//--- substitute t2 and separate t1 ---------
2. t1=(by-cy+((cx-bx)*bdy/bdx))/(cdy-(cdx*bdy/bdx));
//-------------------------------------------
*/
// common
cdx=bx-ax;
cdy=by-ay;
bdx=+cdy;
bdy=-cdx;
//solution 1
t2=(cy-by+((bx-cx)*cdy/cdx))/(bdy-(bdx*cdy/cdx));
dx=bx+bdx*t2;
dy=by+bdy*t2;
//solution 2
t1=(by-cy+((cx-bx)*bdy/bdx))/(cdy-(cdx*bdy/bdx));
dx=cx+cdx*t1;
dy=cy+cdy*t1;
just use the solution that is not dividing by zero ...

After your previous question about the polygons, I started making something, a javascript object. It will show its use here.
I posted it there (I skipped the documentation in this post here, please read the documentation there): Mercator Projection slightly off
I first post the code, I explain later.
<title>Getting coordinates perpendicular to AB</title>
<div id="log"></div>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry"></script>
<script>
Earth = {
// #see http://www.space.com/17638-how-big-is-earth.html for the data
// along the equator
circumference_equator: 40075000,
// throught both poles.
// Note: this is basically the original definition of the meter; they were 2km off on a distance from pole to equator ( http://en.wikipedia.org/wiki/History_of_the_metre )
circumference_poles: 40008000,
// given a change in latitude, how many meters did you move?
lat2Y: function(dLat) {
return this.circumference_poles / 360 * dLat;
},
// given a change in longitude and a given latitude, how many meters did you move?
lng2X: function(dLng, lat) {
return Math.cos( this.deg2rad(lat) ) * (this.circumference_poles / 360 * dLng);
},
// given a distance you move due North (or South), what's the new coordinates?
// returns a change in latitude
y2Lat: function(y) {
return y * 360 / this.circumference_poles;
},
// given a distance you move due East (or West) and a given latitude, what's the new coordinates?
// returns a change in longitude
x2Lng: function(x, lat) {
return x * 360 / ( Math.cos( this.deg2rad(lat) ) * this.circumference_poles);
},
// (360°) degrees to radials
deg2rad: function(deg) {
return deg * Math.PI / 180;
},
// returns a change in position
xy2LatLng: function(y, x, lat) {
return {
lat: this.y2Lat(y),
lng: this.x2Lng(x, lat)
};
},
// #param heading: North = 0; east = 90°; ...
setHeading: function(lat, lng, dist, heading) {
var latDestination = lat + this.y2Lat(dist * Math.cos(this.deg2rad(heading)));
var lngDestination = lng + this.x2Lng(dist * Math.sin(this.deg2rad(heading)), lat);
return {
lat: latDestination,
lng: lngDestination
};
},
// returns the absolute position
moveByXY: function(lat, lng, x, y) {
var dLatLng = Earth.xy2LatLng(x, y, lat);
latLng = [dLatLng.lat, dLatLng.lng ];
return {
lat: lat + latLng[0],
lng: lng + latLng[1]
}
}
}
/**
* returns the shortest distance between a point p and a line segment (u,v).
* based on https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
*/
function distToSegment(p, v, w) {
return Math.sqrt(distToSegmentSquared(p, v, w));
function distToSegmentSquared(p, v, w) {
var l2 = dist2(v, w);
if (l2 == 0) {return dist2(p, v);}
var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) {return dist2(p, v);}
if (t > 1) {return dist2(p, w);}
return dist2(p,
{x: v.x + t * (w.x - v.x),
y: v.y + t * (w.y - v.y)}
);
}
function sqr(x) {
return x * x ;
}
function dist2(v, w) {
return sqr(v.x - w.x) + sqr(v.y - w.y);
}
}
</script>
<script>
var A = {lat: 50.88269282423443, lng: 6.0036662220954895};
var B = {lat: 50.882753744583226, lng: 6.003803014755249};
var C = {lat: 50.88252571592428, lng: 6.003832183778286};
// get the angle of AB (let Google calculate it)
var angle_ab = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(B.lat, B.lng)
);
// we convert these coordinates to metric units. lat goes along y; lng goes along x
// so this tells us that from A to B there are X metres eastwards, Y metres northwards.
var a = {x:0, y:0};
var b = {
x: Earth.lng2X(B.lng - A.lng, A.lat),
y: Earth.lat2Y(B.lat - A.lat),
};
var c = {
x: Earth.lng2X(C.lng - A.lng, A.lat),
y: Earth.lat2Y(C.lat - A.lat),
};
// second, we look for point E, being the projection of C on AB
var dist_E = distToSegment(c, a, b);
// Now we know this: if we move from B, distance "dist_E" on an angle 90° to the right (anti-clockwise) of AB
var D = Earth.setHeading(B.lat, B.lng, dist_E, angle_ab + 90);
log('distance of E (= projection of C on AB) to AB: <b>' + dist_E +'</b>m');
log('Point D: <b>' + D.lat +','+ D.lng +'</b>');
function log(text) {
document.getElementById('log').innerHTML += text + '<br>';
}
</script>
What I did:
first I convert the data from coordinates to metres
I find point E: the projection of C on AB
The distane and angle of CE is the same as BD, so I can use Earth.setHeading(), from B.
NOTICE:
There is no rectangle in your question, but still, notice:
there is no such thing as a rectangle on a curved surface; it is impossible make that rectangle completely accuratly. If you go x distance forward, then turn 90° to the right hand side and repeat that 4 times, you will not get back (exactly) on the point where you started.
On a sphere, the sum of the angles of a rectangle will be greater than 360°; the sum of the angles of a triangle will be greater than 180°.
Simple example: take points (lat, lng) 0,0 ; 0,90 ; 90,0 (two points on the equator + the North Pole); that's a triangle with a sum of angles = 270°.
So, what ever answer you seek, will be an approximation. The bigger the distances, the less accurate the result will be (no matter what genius solves the problem);
You cannot simply assume every right angle on your diagram will be a right angle on the earth's surface.

Thanks to Emmanuel I was able to find the spherical functions which helped me a lot by making the function. I like his answer too but what I finally made was a lot less code.
this.makeRightAngle = function(polygon, e, A, B, C){
var heading_AB = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(B.lat, B.lng)
);
var heading_AC = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var heading_BC = google.maps.geometry.spherical.computeHeading(
new google.maps.LatLng(A.lat, A.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var distanceBC = google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(B.lat, B.lng),
new google.maps.LatLng(C.lat, C.lng)
);
var heading_C_AB = heading_AC - heading_AB;
if((heading_C_AB < 0 && heading_C_AB > -200) || (heading_C_AB > 200)){
var new_heading = heading_AB - 90;
}
else{
var new_heading = heading_AB + 90;
}
var D = google.maps.geometry.spherical.computeOffset(
new google.maps.LatLng(B.lat, B.lng), //From LatLng
distanceBC, //Get distance BC
new_heading //Heading of AB + or - 90 degrees
);
var p = {x: D.lat(), y: D.lng()};
var latlng = new google.maps.LatLng(p.x, p.y);
return latlng;
}
Still many thanks for both answers!

Related

Parametric equation to place a leaflet marker on the circumference of a circle is not precise?

I am working on an application where I have the center of a circle and the radius and I am plotting the circle with the help of Leaflet.
I placed a marker on the north most end of the circumference and made it draggable.
var circle = L.circle(coords, radius).addTo(map);
convertRadiusToLatitude = parseInt(response.radius)/111111;
var coordsOnRadius = [parseFloat(response.lat) + convertRadiusToLatitude, parseFloat(response.long)];
var markerOnRadius = L.marker(coordsOnRadius, {draggable: true}).addTo(map);
Now, this adds the marker to the circumference and now I wanted it to be draggable only on the circumference itself for which I used the parametric equation.
Parametric equation
x = Xc + R * cos(theta)
y = Yc + R * sin(theta)
Code for dragging
markerOnRadius.on('drag', function(e){
bearing = marker.getLatLng().bearingTo(markerOnRadius.getLatLng());
var markerOnRadiusX = parseFloat(response.lat) + ((0.000009 * parseFloat(response.radius)) * Math.cos( toRad(bearing) ));
var markerOnRadiusY = parseFloat(response.long) + ((0.000009 * parseFloat(response.radius)) * Math.sin( toRad(bearing) ));
markerOnRadius.setLatLng([markerOnRadiusX, markerOnRadiusY]);
});
The bearingTo method:
L.LatLng.prototype.bearingTo = function(other) {
var d2r = L.LatLng.DEG_TO_RAD;
var r2d = L.LatLng.RAD_TO_DEG;
var lat1 = this.lat * d2r;
var lat2 = other.lat * d2r;
var dLon = (other.lng-this.lng) * d2r;
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x);
brng = parseInt( brng * r2d );
brng = (brng + 360) % 360;
return brng;
};
Issue
When I start dragging the marker, this code is working fine and brings it back to the circumference at the bearing at which the marker is dragged to. But there is one problem, the coords on the circumference are slightly off and in terms of longitude. When the bearing is 0 (north), the coords are perfect, but when it is 90 (east), the longitude is slightly less that it should for the marker to be at the circumference.
Again at 180 (south), coords are perfect, but at 270 (west), the longitude calculated is slightly less and the marker tends towards the radius again.
So basically if you visualize the marker being dragged, it starts perfectly on the north end and starts coming inside the circle slightly increasing with the bearing till it reacher 90 and then starts going towards the circumference again till 180 when it is perfect again.
It forms more like a ellipse if you get the gist of it.
Could anyone tell me why is longitude coming a little off and why the marker moves in an elliptical path. Has it something to do with the world coordinates and window coordinates. Or are my equations slightly off somewhere?
It does look like a projection issue. In your dragging code you are basically doing
lat = a + r cos(baring)
long = b + r sin(baring)
giving a circle in the Lat-Long coordinates. This would work fine if you were at the equator with Mercator projection. You will get more distortion as you move further towards the polls.
Assume you are using the defaults for Leaflet reference doc You have the EPSG3857 Web Mercator coordinates.
If you want to ensure you have a exact circle it will be better to work using screen coordinates. You can get these using methods on the ICRS objects. First get the coordinate system L.CRS.EPSG3857 and use the latLngToPoint and pointToLatLng methods.
var crs = L.CRS.EPSG3857;
var zoom = ...; // how you calculate your zoom factor
markerOnRadius.on('drag', function(e){
var markerLL = marker.getLatLng()
var morLL = markerOnRadius.getLatLng();
var markerP = crs.latLngToPoint(markerLL,zoom);
var morP = crs.latLngToPoint(morLL,zoom);
// get the distance between the two points
var dist = markerP.distanceTo(morP);
// Get the vector from center to point
var A = morP.subtract(markerP);
// scale so its of the desired length
var B = A. multiplyBy( factor / dist);
// Add on the center
var C = markerP.add(B);
// Convert back to LatLong
var D = crs.pointToLatLng(C,zoom);
markerOnRadius.setLatLong(D);
});

swapping one library out for another

relatively simple task here but seeing as i'm just getting to grips with object oriented programming it is perplexing me. Im currently using the first function of lon_lat_to_cartesian:
function lonLatToVector3( lng, lat, out )
{
out = out || new THREE.Vector3();
//flips the Y axis
lat = PI / 2 - lat;
//distribute to sphere
out.set(
Math.sin( lat ) * Math.sin( lng ),
Math.cos( lat ),
Math.sin( lat ) * Math.cos( lng )
);
return out;
}
I call it within my glmain.js file using the following line:
position = lonLatToVector3(data.latitude, data.longitude);
(ie the latititude and longitude points given to it are turned into a vector)
I now wish to swap this library for latlon-vectors.js. Its the following lines(50-60) I want to use:
LatLon.prototype.toVector = function() {
var φ = this.lat.toRadians();
var λ = this.lon.toRadians();
// right-handed vector: x -> 0°E,0°N; y -> 90°E,0°N, z -> 90°N
var x = Math.cos(φ) * Math.cos(λ);
var y = Math.cos(φ) * Math.sin(λ);
var z = Math.sin(φ);
return new Vector3d(x, y, z);
};
to my limited newby knowledge this appears to be a method of the main object:
function LatLon(lat, lon) {
// allow instantiation without 'new'
if (!(this instanceof LatLon)) return new LatLon(lat, lon);
this.lat = Number(lat);
this.lon = Number(lon);
}
I'd have no problem calling this, i could just do:
position = LatLon(data.latitude, data.longitude);
but that would not accomplish my aim and convert my Lat, Lon points to a vector. how do I go on to call the aforementioned lines (50-60)?
looking at the code in latlon-vectors.js, in order to get the lat/lon into a vector you need to call:
var position = LatLon(data.latitude, data.longitude);
var vector = position.toVector();

Drawing a line with perpendicular decoration

I need to draw a line in the following manner:
 
For now, it will be only drawn in code, no user input.
My question is, how to draw perpendiculars to a line, if I draw it point by point? (Obviously, this will be the case, because drawing with bezier curves will not give me the possibility to somehow impact the drawing).
The closest answer I found was possibly this one, but I can't reverse the equations to derive C. Also there is no length of the decoration mentioned, so I think this will not work as I'd like it to.
Find the segment perpendicular to another one is quite easy.
Say we have points A, B.
Compute vector AB.
Normalize it to compute NAB (== the 'same' vector, but having a length of 1).
Then if a vector has (x,y) as coordinates, its normal vector has (-y,x) as coordinates, so
you can have PNAB easily (PNAB = perpendicular normal vector to AB).
// vector AB
var ABx = B.x - A.x ;
var ABy = B.y - A.y ;
var ABLength = Math.sqrt( ABx*ABx + ABy*ABy );
// normalized vector AB
var NABx = ABx / ABLength;
var NABy = ABy / ABLength;
// Perpendicular + normalized vector.
var PNABx = -NABy ;
var PNABy = NABx ;
last step is to compute D, the point that is at a distance l of A : just add l * PNAB to A :
// compute D = A + l * PNAB
var Dx = A.x + l* PNAB.x;
var Dy = A.y + l *PNAB.y;
Updated JSBIN :
http://jsbin.com/bojozibuvu/1/edit?js,output
Edit :
A second step is to draw the decorations at regular distance, since it's Christmas time, here's how i would do it :
http://jsbin.com/gavebucadu/1/edit?js,console,output
function drawDecoratedSegment(A, B, l, runningLength) {
// vector AB
var ABx = B.x - A.x;
var ABy = B.y - A.y;
var ABLength = Math.sqrt(ABx * ABx + ABy * ABy);
// normalized vector AB
var NABx = ABx / ABLength;
var NABy = ABy / ABLength;
// Perpendicular + normalized vector.
var PNAB = { x: -NABy, y: NABx };
//
var C = { x: 0, y: 0 };
var D = { x: 0, y: 0 };
//
drawSegment(A, B);
// end length of drawn segment
var endLength = runningLength + ABLength;
// while we can draw a decoration on this line
while (lastDecorationPos + decorationSpacing < endLength) {
// compute relative position of decoration.
var decRelPos = (lastDecorationPos + decorationSpacing) - runningLength;
// compute C, the start point of decoration
C.x = A.x + decRelPos * NABx;
C.y = A.y + decRelPos * NABy;
// compute D, the end point of decoration
D.x = C.x + l * PNAB.x;
D.y = C.y + l * PNAB.y;
// draw
drawSegment(C, D);
// iterate
lastDecorationPos += decorationSpacing;
}
return ABLength;
}
All you need is direction of curve (or polyline segment) in every point, where you want to draw perpendicular.
If direction vector in point P0 is (dx, dy), then perpendicular (left one) will have direction vector (-dy, dx). To draw perpendicular with length Len, use this pseudocode:
Norm = Sqrt(dx*dx + dy*dy) //use Math.Hypot if available
P1.X = P0.X - Len * dy / Norm
P1.Y = P0.Y + Len * dx / Norm
P.S. If you know direction angle A, then direction vector
(dx, dy) = (Cos(A), Sin(A))
and you don't need to calculate Norm, it is equal to 1.0

How to plot points on a 3D sphere with ThreeJS

I just recently started delving into ThreeJS. Currently I'm trying to plot a point on a sphere but it appears to be plotting in the southern hemispehre instead of the northern hemisphere. Vertically, it looks to be in the correct spot just on the bottom of the sphere instead of the top. I grabbed some code from this answer: https://stackoverflow.com/a/8982005/738201
In the image the yellow line with the red box is my plotted point. It should be in the upstate NY region instead of where it is now.
And lastly, the code.
function xyz(lat, lon){
var cosLat = Math.cos(lat*Math.PI/180.00);
var sinLat = Math.sin(lat*Math.PI/180.00);
var cosLon = Math.cos(lon*Math.PI/180.00);
var sinLon = Math.sin(lon*Math.PI/180.00);
var r = sphere.geometry.radius; //50
var coords = {};
coords.x = r * cosLat * cosLon;
coords.y = r * cosLat * sinLon;
coords.z = r * sinLat;
console.log(coords);
return coords;
}
// Lat/Lon from GoogleMaps
var coords = xyz(42.654162, -73.699830);
// returns {x: 10.321018160637124, y: -35.29474079777381, z: 33.878575178802286}
I suspect that the issue may be using 2D coords on a 3D sphere but if so, I'm not quite sure how to rectify that.
Try this, It will help to use directly lat and long on the map
function calcPosFromLatLon(phi, theta){
let lat = (90 - phi) * (Math.PI/180);
let lon = (theta + 180) * (Math.PI/180);
const x = -(Math.sin(lat)* Math.cos(lon))
const z = Math.sin(lat) * Math.sin(lon)
const y = Math.cos(lat)
}
calcPosFromLatLon(//coordinates here//)

Google Maps Two Circles Intersection Points

Is there an easy way to get the lat/lng of the intersection points (if available) of two circles in Google Maps API V3? Or should I go with the hard way?
EDIT : In my problem, circles always have the same radius, in case that makes the solution easier.
Yes, for equal circles rather simple solution could be elaborated:
Let's first circle center is A point, second circle center is F, midpoint is C, and intersection points are B,D. ABC is right-angle spherical triangle with right angle C.
We want to find angle A - this is deviation angle from A-F direction. Spherical trigonometry (Napier's rules for right spherical triangles) gives us formula:
cos(A)= tg(AC) * ctg(AB)
where one symbol denote spherical angle, double symbols denote great circle arcs' angles (AB, AC). We can see that AB = circle radius (in radians, of course), AC = half-distance between A and F on the great circle arc.
To find AC (and other values) - I'll use code from this excellent page
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
and our
AC = c/2
If circle radius Rd is given is kilometers, then
AB = Rd / R = Rd / 6371
Now we can find angle
A = arccos(tg(AC) * ctg(AB))
Starting bearing (AF direction):
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x);
Intersection points' bearings:
B_bearing = brng - A
D_bearing = brng + A
Intersection points' coordinates:
var latB = Math.asin( Math.sin(lat1)*Math.cos(Rd/R) +
Math.cos(lat1)*Math.sin(Rd/R)*Math.cos(B_bearing) );
var lonB = lon1.toRad() + Math.atan2(Math.sin(B_bearing)*Math.sin(Rd/R)*Math.cos(lat1),
Math.cos(Rd/R)-Math.sin(lat1)*Math.sin(lat2));
and the same for D_bearing
latB, lonB are in radians
The computation the "hard" way can be simplified for the case r1 = r2 =: r. We still first have to convert the circle centers P1,P2 from (lat,lng) to Cartesian coordinates (x,y,z).
var DEG2RAD = Math.PI/180;
function LatLng2Cartesian(lat_deg,lng_deg)
{
var lat_rad = lat_deg*DEG2RAD;
var lng_rad = lng_deg*DEG2RAD;
var cos_lat = Math.cos(lat_rad);
return {x: Math.cos(lng_rad)*cos_lat,
y: Math.sin(lng_rad)*cos_lat,
z: Math.sin(lat_rad)};
}
var P1 = LatLng2Cartesian(lat1, lng1);
var P2 = LatLng2Cartesian(lat2, lng2);
But the intersection line of the planes holding the circles can be computed more easily. Let d be the distance of the actual circle center (in the plane) to the corresponding point P1 or P2 on the surface. A simple derivation shows (with R the earth's radius):
var R = 6371; // earth radius in km
var r = 100; // the direct distance (in km) of the given points to the intersections points
// if the value rs for the distance along the surface is known, it has to be converted:
// var r = 2*R*Math.sin(rs/(2*R*Math.PI));
var d = r*r/(2*R);
Now let S1 and S2 be the intersections points and S their mid-point. With s = |OS| and t = |SS1| = |SS2| (where O = (0,0,0) is the earth's center) we get from simple derivations:
var a = Math.acos(P1.x*P2.x + P1.y*P2.y + P1.z*P2.z); // the angle P1OP2
var s = (R-d)/Math.cos(a/2);
var t = Math.sqrt(R*R - s*s);
Now since r1 = r2 the points S, S1, S2 are in the mid-plane between P1 and P2. For v_s = OS we get:
function vecLen(v)
{ return Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z); }
function vecScale(scale,v)
{ return {x: scale*v.x, y: scale*v.y, z: scale*v.z}; }
var v = {x: P1.x+P2.x, y: P1.y+P2.y, z:P1.z+P2.z}; // P1+P2 is in the middle of OP1 and OP2
var S = vecScale(s/vecLen(v), v);
function crossProd(v1,v2)
{
return {x: v1.y*v2.z - v1.z*v2.y,
y: v1.z*v2.x - v1.x*v2.z,
z: v1.x*v2.y - v1.y*v2.x};
}
var n = crossProd(P1,P2); // normal vector to plane OP1P2 = vector along S1S2
var SS1 = vecScale(t/vecLen(n),n);
var S1 = {x: S.x+SS1.x, y: S.y+SS1.y, z: S.z+SS1.z}; // S + SS1
var S2 = {x: S.x-SS1.x, y: S.y-SS2.y, z: S.z-SS1.z}; // S - SS1
Finally we have to convert back to (lat,lng):
function Cartesian2LatLng(P)
{
var P_xy = {x: P.x, y:P.y, z:0}
return {lat: Math.atan2(P.y,P.x)/DEG2RAD, lng: Math.atan2(P.z,vecLen(P_xy))/DEG2RAD};
}
var S1_latlng = Cartesian2LatLng(S1);
var S2_latlng = Cartesian2LatLng(S2);
Yazanpro, sorry for the late response on this.
You may be interested in a concise variant of MBo's approach, which simplifies in two respects :
firstly by exploiting some of the built in features of the google.maps API to avoid much of the hard math.
secondly by using a 2D model for the calculation of the included angle, in place of MBo's spherical model. I was initially uncertain about the validity of this simplification but satisfied myself with tests in a fork of MBo's fiddle that the errors are minor at all but the largest of circles with respect to the size of the Earth (eg at low zoom levels).
Here's the function :
function getIntersections(circleA, circleB) {
/*
* Find the points of intersection of two google maps circles or equal radius
* circleA: a google.maps.Circle object
* circleB: a google.maps.Circle object
* returns: null if
* the two radii are not equal
* the two circles are coincident
* the two circles don't intersect
* otherwise returns: array containing the two points of intersection of circleA and circleB
*/
var R, centerA, centerB, D, h, h_;
try {
R = circleA.getRadius();
centerA = circleA.getCenter();
centerB = circleB.getCenter();
if(R !== circleB.getRadius()) {
throw( new Error("Radii are not equal.") );
}
if(centerA.equals(centerB)) {
throw( new Error("Circle centres are coincident.") );
}
D = google.maps.geometry.spherical.computeDistanceBetween(centerA, centerB); //Distance between the two centres (in meters)
// Check that the two circles intersect
if(D > (2 * R)) {
throw( new Error("Circles do not intersect.") );
}
h = google.maps.geometry.spherical.computeHeading(centerA, centerB); //Heading from centre of circle A to centre of circle B. (in degrees)
h_ = Math.acos(D / 2 / R) * 180 / Math.PI; //Included angle between the intersections (for either of the two circles) (in degrees). This is trivial only because the two radii are equal.
//Return an array containing the two points of intersection as google.maps.latLng objects
return [
google.maps.geometry.spherical.computeOffset(centerA, R, h + h_),
google.maps.geometry.spherical.computeOffset(centerA, R, h - h_)
];
}
catch(e) {
console.error("getIntersections() :: " + e.message);
return null;
}
}
No disrespect to MBo by the way - it's an excellent answer.

Categories

Resources