How can I refer to the previous element in an array?
I want to calculate the angular distance of 2 coordinates so that I can move a steering wheel.
My data comes from a robot.
let arrX = [];
let arrY = [];
tag5617.subscribe(function(message) {
let X = document.getElementById('posX');
X.innerHTML = message.x.toFixed(2);
let Y = document.getElementById('posY');
Y.innerHTML = message.y.toFixed(2);
myChart.data.datasets[0].data.push({x:message.x.toFixed(2), y:message.y.toFixed(2)});
arrX.push(message.x);
arrY.push(message.y);
let deg = Math.atan2(message.x - arrX[-1], message.y - arrY[-1]) * 180 / Math.PI;
myChart.update();
});
After you push a new element onto an array, the element before it is the 2nd to last element.
Unlike some other languages (e.g. Python) there's no shorthand syntax to index from the end of an array. So you have to calculate the index by subtracting from array.length.
let deg = Math.atan2(message.x - arrX[arrX.length-2], message.y - arrY[arrY.length-2]) * 180 / Math.PI;
How do i identify if a point with a LAT, LONG coordinate format is near to other point.
Lets Say i want to find all the points near to:
-38.9086621 ,-68.082214
That are at less than 1km of distance. I already know how to find a coordinate in a given quadrant but not how to find a quadrant near a point.
You can use the haversine formular to calculate distances between two given coordinates. The calculated distance is the direct connection between the given coordinates (beeline).
JavaScript example (Source):
var R = 6371e3; // meters
var lat1Radians = lat1.toRadians();
var lat2Radians = lat2.toRadians();
var deltaLat = (lat2-lat1).toRadians();
var deltaLon = (lon2-lon1).toRadians();
var a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
Math.cos(lat1Radians) * Math.cos(lat2Radians) *
Math.sin(deltaLon/2) * Math.sin(deltaLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
Let's say that i have a database of 100,000 coordinates,
i need to find which ones are less than 1 mile away from a certain coordinate.
What's the most efficient way to do this?(in any language)
Firstly, we have to write a basic function to calculate the distance between 2 points:
function distance(lat1, lon1, lat2, lon2) {}
For this example, I will base the function on the formula of a spherical earth projected to a plane (here)
Firstly, we have to calculate the deltas (the distance in degrees between the lat and longs) and the mean latitude (average of latitudes):
var dLat = lat1 - lat2;
var dLon = lon1 - lon2;
var mLat = (lat1 + lat2) / 2;
var earthRadius = 3959; //in miles
Then we convert those to Radians using d=180/PI rad:
dLat = dLat * 180 / 3.1415926535;
dLon = dLon * 180 / 3.1415926535;
mLat = mLat * 180 / 3.1415926535;
Now, we use the formula to transform our data into distance:
var distance = earthRadius * (dLat * dLat + Math.pow(Math.cos(mLat) * dLon, 2));
And return the distance
return distance;
Now, just iterating through all the points and checking if the distance is ok for each one. Let's say a point is described in this way:
var p = {
lat = ...
lon = ...
}
And assuming there is a list of points (for example, named points) and a reference point (for example, named ref).
var result = []
points.forEach(function (d) {
if (distance(d.lat, d.lon, ref.lat, ref.lon) <= 1) {
result.push(d);
}
};
You can also check for latitude boundary box - longtitude requires more complex calculations and it's just a waste of time. You can determine a mile in degrees is 1/69 deg/mile (approximately 0.1449 degrees). So you can check which points are outside of this boundary box:
var result = []
var maxLat = ref.lat + 0.1449;
var minLat = ref.lat - 0.1449;
points.forEach(function (d) {
if (d.lat > maxLat || d.lat < minLat) continue;
if (distance(d.lat, d.lon, ref.lat, ref.lon) <= 1) {
result.push(d);
}
};
Then you should finish with an array of points that are closer than 1 mile from the reference point.
I might have a mistake in the formula things (I'm more like a programmer than a mathematican). So double check if they work with the Wikipedia article I added a link to.
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.
The Google Earth Desktop Application shows the both the map length and ground length of a line.
In the Google Earth plugin I want to do a similar thing, that is I wish to determine the ground length of a tessellated KmlLineString taking the terrain into account.
Can I do this, and if so, how?
You can certainly get the length pretty easily if you use the earth-api-utility-library. Using that you can do.
var length = (new geo.Path(linestring)).distance();
Granted this method does not take the terrain into account - but there are a number of caveats you should be aware of before trying calculate distances using an elevation gradient.
Firstly any differences between topographic and direct distance are minimal in most cases. Indeed many quality GPS receivers simply don't take any changes in elevation into account when calculating distances.
Secondly ground altitude is one of the most unreliable pieces data. Using a gradient based on elevation to determine distance will often produce greater inaccuracy in distance measurements than using a simple 'as the crow flies' measure.
Bearing that in mind, if you still wanted to do it then one way would be something like the following.
Sample the line string at certain points (say every 10 meters).
Get the ground altitude at each point.
Convert each point to Cartesian coordinates
Calculate the angular distances between each Cartesian point in sequence.
You can improve your precision of this kind of method in two ways, either by increasing the sampling rate (say every meter) or by applying a smoothing procedure to the results.
For a rougher version, you could just loop over the coordinates in the the KmlLinestring itself, rather than resampling at some set distance. You would use the latitude, longitude of the coordinate to get the ground altitude at each point. Then you would construct a Cartesian coordinate from this data (latitude, longitude, elevation => X,Y,Z) and work out the angular distance between it and the next point...and so on.
something like the following idea should work - although it is written here and untested!
var EARTH_RADIUS = 6378135; // approximate in meters
var degreestoRadians = function(degrees) {
return degrees * Math.PI / 180;
}
var arcLength = function(point1 , point2) {
var length = Math.sqrt(Math.pow(point1.X-point2.X, 2)
+ Math.pow(point1.Y-point2.Y, 2)
+ Math.pow(point1.Z-point2.Z, 2));
var angle = 2 * Math.asin(length/2/EARTH_RADIUS);
return EARTH_RADIUS * angle;
}
var sphericalToCartesian = function(latitude, longitude, altitude) {
var phi = degreestoRadians(latitude);
var theta = degreestoRadians(longitude);
var rho = EARTH_RADIUS + altitude;
return {
X: Math.cos(phi) * Math.cos(theta) * rho,
Y: Math.cos(phi) * Math.sin(theta) * rho,
Z: Math.sin(phi) * rho
}
}
var topographicDistance = function(linestring) {
var coordinates = linestring.getCoordinates(); //KmlCoordArray
var last = null;
var distance = 0;
for(var i = 0; i < coordinates.length; i++) {
var coord = coordinates.get(i); //KmlCoord
var lat = coord.getLatitude();
var lng = coord.getLongitude();
var alt = ge.getGlobe().getGroundAltitude(lat, lng);
var latest = sphericalToCartesian(lat, lng, alt);
if(last != null) {
distance += arcLength(last, latest);
}
last = latest;
}
return distance;
}
You would use it like so...
var distance = topographicDistance(yourLinestring);