How to find the nearest points to given coordinates - javascript

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;

Related

Mysql polygon between multiple coordinates

I have 4 points on the map(latitude, longitude), there can be a more.
And I need to build a MySQL POLYGON with 10 Kilometers wide between these points.
As you see from the picture - I have blue line, and I need a polygon (green outline).
My first thought was to create two separate points for each given point.
For example:
Given 1st point(Bremen)
calculate 5km right and add a point,
calculate 5km left and add a point.
Logic is simple.
BUT the problem is - I don't know what to calculate(right,top,bottom,left), i need some kind of angle, but I am stuck here.
I just need an algorithm, no need for a full code example.
function in javascript i tried:
var meters = 10000 / 2;
var my_lat = 52.51978;
var my_long = 13.388211;
// number of km per degree = ~111km (111.32 in google maps, but range varies
/* between 110.567km at the equator and 111.699km at the poles) */
// 1km in degree = 1 / 111.32km = 0.0089
// 1m in degree = 0.0089 / 1000 = 0.0000089
var coef = meters * 0.0000089;
var new_lat = my_lat + coef;
// pi / 180 = 0.018
var new_long = my_long - coef / Math.cos(my_lat * 0.018);

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);
});

Leaflet calculating meters per pixel at zoom level

I am trying to determine a way to calculate the number of meters represented by 1 pixel at a given zoom level and geo centerpoint in Leaflet. Could anyone direct me to the math involved or if there is a way to do this out of the box in leaflet? I am not finding much out there.
You can use the containerPointToLatLng conversion function of L.Map to get the latLngcoordinates for a given pixelcoordinate. If you take one of the first pixel, and one of the next, you can use the distanceTo utility method of L.LatLng to calculate the distance in meters between them. See the following code (assuming map is an instance of L.Map):
var centerLatLng = map.getCenter(); // get map center
var pointC = map.latLngToContainerPoint(centerLatLng); // convert to containerpoint (pixels)
var pointX = [pointC.x + 1, pointC.y]; // add one pixel to x
var pointY = [pointC.x, pointC.y + 1]; // add one pixel to y
// convert containerpoints to latlng's
var latLngC = map.containerPointToLatLng(pointC);
var latLngX = map.containerPointToLatLng(pointX);
var latLngY = map.containerPointToLatLng(pointY);
var distanceX = latLngC.distanceTo(latLngX); // calculate distance between c and x (latitude)
var distanceY = latLngC.distanceTo(latLngY); // calculate distance between c and y (longitude)
That should work, thanks to Jarek PiĆ³rkowski for pointing my mistake before the edit.
You can use this to work out the metres per pixel:
metresPerPixel = 40075016.686 * Math.abs(Math.cos(map.getCenter().lat * Math.PI/180)) / Math.pow(2, map.getZoom()+8);
Take a look at openstreetmap.org page on zoom levels. It gives this formula for calculating the meters per pixel:
The distance represented by one pixel (S) is given by
S=C*cos(y)/2^(z+8) where...
C is the (equatorial) circumference of the Earth
z is the zoom level
y is the latitude of where you're interested in the scale.
Correct me if I am wrong, IMHO, the number of meters per pixel = map height in meters / map height in pixels
function metresPerPixel() {
const southEastPoint = map.getBounds().getSouthEast();
const northEastPoint = map.getBounds().getNorthEast();
const mapHeightInMetres = southEastPoint.distanceTo(northEastPoint);
const mapHeightInPixels = map.getSize().y;
return mapHeightInMetres / mapHeightInPixels;
}

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.

Compute a distance including elevation/terrain data for a KmlLinestring

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);

Categories

Resources