Projecting a point onto a path - javascript

Suppose I have an ordered array containing points (lat, lon) describing a path, and I also have a point (lat, lon) describing my current location.
How can I project the point onto the path (and place the point in the appropriate place in the array)?
What I tried is just simply by searching for the nearest two points and assume it's in the middle of them. It's a good guess, but sometimes fails.
What would be a good way of doing this?

I see it like this:
p0,p1 are path line segment endpoints
p is your position
q' closest point on line in 3D cartessian
q is q' corrected by spherical projection
So:
convert points to 3D cartessian coordinates
compute perpendicular distance from point and line
q'=p0+(dot(p-p0,p1-p0)*(p1-p0)/(|p-p0|*|p1-p0|))
perpendicular_distance = |p-q'|
find segment with smallest perpendicular_distance
and use only it for the rest of bullets
compute q
If you use sphere instead of ellipsoid then you already know the radius if not then either compute the radius algebraically or use average:
r=0.5*(|p0-(0,0,0)|+|p1-(0,0,0)|)
assuming (0,0,0) is Earth's center. You can also be more precise if you weight by position:
w=|q'-p0|/|p1-p0|
r=(1-w)*|p0-(0,0,0)|+w*|p1-(0,0,0)|
now just correct the position of q'
q=q'*r/|q'|
set vector q' as q with size r if it is not obvious enough. Also |p0-(0,0,0)|=|p0| obviously but I wanted to be sure you get how I got it ...
convert q from Cartesian to spherical coordinates
[Notes]
|a| is size of vector a done like: |a|=sqrt(ax*ax+ay*ay+az*az)
dot(a,b) is dot product of vectors a,b done like: dot(a,b)=(a.b)=ax*bx+ay*by+az*bz
if your path is not too complex shaped then you can use binary search to find the closest segment.
For distance comparison you do not need the sqrt ...

Find the path segment which is closest to the current position.
Distance from point to a line

Related

Building an arc (an array of segments) at three points

I have the coordinates of three points. I want to build an arc on them
An arc (non-ideal) consists of segments (let 10 segments).
I need having the coordinates of three points to get an array of arc segments at these points
How can I do this?
A way, definitely no the shortest way, could be the following.
You have 3 point, thus you can write the circonference equation(x2+y2+ax+by+c=0) (for three points pass only a circonference). This is a matter of solving a system of 3 equations in three unknowns. Now given the 2 outermost points,(x1,y1) and (x2,y2), you can calculate the cord length with Pitagora theorem:
cord = √(y2-y1)2+(x2-x1)2
Now use the theorem of the cord to find the angle in the center that I call alpha thus:
sin(alpha/2) = cord/2*radius
Having the circonference equation is easy to calculate the radius.
Now that you have alpha you can split the angle in 20 parts. For example if alpha is 160 you get Beta=8
Now you can do:
P0 x1,y1
P1 cos(180-8),sin(180-8)
P2 cos(180-16),sin(180-16)
.
.
.
and so on until to 160 in my example.
But Attention! Those above are relative coordinates, relative to the center of the circonference. Called the center (c1,c2) the coordinates will be:
P0 x1,y1
P1 c1+cos(180-8),c2+sen(180-8)
P2 c1+cos(180-16),c2+sin(180-16)
.
.
.

Geometry on Latitude/Longitude (Projection of point on arc)

I just want to check if there is a point (lat, long) of the projection intersecting with the arc giving by 2 points (lat, long) and if it does, I want to find that (lat, long).
Can (lat, long) be used as a 2D vector space to make this problem similar to the one in cartesian co-ordinates? How accurate would it be?
While the answer on Link helps with getting the distance to the arc, how can I know whether the point of intersection is between the points that were used to find the great circle? Also would it be possible to solve this without having to use switch to cartesian co-ordinates?
There are two ways to approach this.
The first assumes a straight line between the two points--although, in actuality, such a line would intersect with the earth.
The second actually determines the great-circle route between the two points, that is, the minimum-length arc that actually follows the earth's surface and joins the two points. To do this, you have to use coordinate transformations to generate vectors of direction cosines for the two superficial points. Call them A and B.
To determine whether C lies on that arc, you can't just do linear interpolation like you could if you cheated and used a line segment that passes through the earth. Instead, you need to calculate the direction cosines for C also. C lies true between A and B if angles AC, BC, and AB are all equal. Angles can be determined by calculating the dot products of the corresponding direction cosines and evaluating the inverse cosine thereof.

Compute Voronoï diagram on a large dataset of very close points

I have a large dataset of geographical points (around 22 000 points, but I could be more in the future) and I need to compute their Voronoï diagram. I first project my points from (lat,lng) to (x,y) (using latLngToLayerPoint() from Leaflet) and then compute the diagram based on a Javascript implementation of Fortune's algorithm . I recover each cells of the diagrams or more precisely va and vb, being respectively :
"A Voronoi.Vertex object with an x and a y property defining the start
point (relative to the Voronoi site on the left) of this Voronoi.Edge
object."
and
"A Voronoi.Vertex object with an x and a y property defining the end
point (relative to Voronoi site on the left) of this Voronoi.Edge
object."
(cf. Documentation)
Finally, I project back these points to display the diagram using leaflet. I know that, in order to compute the diagram each point needs to be unique, so I get rid of duplicates before computing the diagram. But the thing is, I end up with a pretty bad result (non-noded intersections, complex polygons):
Close-up
I have holes in the diagram and I'm not sure why. The points are house Address so some of them, even if they are not equals, are really (really) close. And I wonder if the issue doesn't come from the projection (if (lat1,lng1) and (lat2,lng2) are almost equals, will (x1,y1) and (x2,y2) be equals ?). I strongly suspect that is where the issue come from, but I don't know how to workaround (establish a threshold ?)
Edit : I precise that I delete the duplicates after the projection, so it's not about the precision of the projection but more about what happen if two points are one-pixel apart ?
So I found the solution to my problem, I post it in case of anyone need to compute a Voronoï diagram on a map using Leaflet and Turf and is having troubles implementing the Fortune's algorithm (until turf-voronoi works).
Other sources of how to compute a Voronoï diagram on map can be found (but using d3) (I think d3 also use this Javascript implementation of Fortune's algorithm)
The problem was not caused by the size of the dataset or the proximity of the points, but by how I recovered the cells.
So you first need to project your point from (lat,lng) to (x,y)(using latLngToLayerPoint()), compute the diagram : voronoi.compute(sites,bbox), where the sites are your points looking like this [ {x: 200, y: 200}, {x: 50, y: 250}, {x: 400, y: 100} /* , ... */ ] (note that your sites needs to be unique) and if you want the frame of the screen for your current zoom to be your bbox juste use :
var xl = 0,
xr = $(document).width(),
yt = 0,
yb = $(document).height();
Once you computed the diagram, just recover the cells (be carfull, if you want the right polygons you need the edges to be counterclockwise ordered (or clockwise ordered, but you them to be ordered), thankfully the algorithm provides the half edges of a given Voronoï.Vertex counterclockwise ordered). To recover the vertex of each cell you can use either getStartpoint() or getEndpoint() without forgetting to project them back from (x,y) to (lat,lng) (using layerPointToLatLng())
diagram.cells.forEach(function (c) {
var edges=[];
var size = c.halfedges.length;
for (var i = 0; i < size; i++) {
var pt = c.halfedges[i].getEndpoint();
edges.push(map.layerPointToLatLng(L.point(pt.x,pt.y)));
};
voronoi_cells.push(L.polygon(edges));
});
Finally, you have to use a FeatureCollection to display the diagram :
I highly recomment you don't implement a Voronoi tesselation algorithm by yourself, and use https://github.com/Turfjs/turf-voronoi instead.

Interconnected curved lines

Given a series of JSON co-ordinates typically in the format:
{from: {x:0, y:0}, to: {x:0, y:10}, ...}
I would like to draw a series of straight dotted paths which are connected with simple, fixed radius rounded corners. I have been looking at Slope Intercept Form to calculate the points along the straight line but I am a little perplexed as to the approach for calcualting the points along the (Bezier?) curves.
e.g. I want to draw curves between p1 and p2 and p3 and p4. Despite what the poor mockup might imply I am happy for the corners to be a fixed radius e.g. 10px
I would like to abstract out the drawing logic and therefore am seeking a generalised approach to returning a JavaScript point array which I can then render in a number of ways (hence I am avoiding using any inbuilt functions provided by SVG, Canvas etc).
What you want is a cubic bezier curve.
http://www.blackpawn.com/texts/splines/
Look at the first applet on this page. If A is p1, D is p2, the direction A-B is line 1's angle and the direction C-D is line 2's angle you can see how this gives you the properties you need - it starts at angle 1 and ends at angle 2 and is flush with the points.
So, to get your points C and D, one way to do this would be to take the line segment 1, copy it, place it starting at p1 - and say where the new line ends is B, and similar with line segment 2 and p2 for D. (And you could do things like have a factor that multiplies into the copied line segments' distance to make the curves stick out more or less... etc)
Then just do the math :)
http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
And once you have your equation for the curve, step through it with a delta t of the desired precision (e.g. every 0.1 of t, every 0.01...) and spit out every pair of points on the curve as a line segment.

How to check if a given marker is inside a area

Hello everybody o/
I know that this is more a math question than gmap, but I suppose that someone already pass through this =)
In my map, I have circle (actually I have several of them, but this not change the question), like this: http://code.google.com/intl/pt-BR/apis/maps/articles/mvcfun/step6.html
How do I know if a marker (with latitude X and longitude Y) is inside this circle?
Sorry for the bad english, I'm brazillian =p
In Google Maps JavaScript API v3 you can use geometry library. To enable it you have to slightly change the script URL:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry"></script>
The library contains utility functions for the computation of geometric data on sphere. You can utilize it to compute the distance of two points given by their latLngs this way:
var distanceInMetres = google.maps.geometry.spherical.computeDistanceBetween(latLngCircleCenter, latLngPoint);
Now you can easily check if the point is inside the circle (suppose R is in metres):
if(distanceInMetres < R)
alert("in the circle");
If (lat1, lon1) and (lat2, lon2) are your two points and R is the radius of the circle around your first point, then the distance between the points is given by the haversine formula (or the Great-circle distance). But I believe that for your problem, the angles are small enough to use this approximation:
and then check whether d^2 is less than the radius R^2.
But if your latitude and longitude differences are larger than a few degrees, you'll want to use the full haversine formula.
I Recommend you read http://www.movable-type.co.uk/scripts/latlong.html. It provides a number of algorithms for computations of this kind. It includes JavaScript code for the computations.
Basically if you have the coords of circle center (cX,cY) and radius R, and some marker at X,Y you can do the following calculations:
var distanceQuad = (X-cX)*(X-cX)+(Y-cY)*(Y-cY)
if (distanceQuad<=(R*R))
{
alert("Marker inside circle!");
}
This is from trigonometry. You calculate distance as sqrt(sqr(deltaX)+sqr(deltaY)) and compare it with circle Radius. Given code is a bit optimized to get rid of calculating square root.
It's much easier than you'd expect. Read this answer including a working jsFiddle example.

Categories