I have a given center in the map [x1,y1]. From that center I am drawing a circle with a 1 mile radius. I need to generate 8 more points around the circle, the distance between the individual points to center should be 1 mile, so they are on the circle bounds. I do know the formulas to get x2, y2 but the problem is it doesn't apply to earth's map since it isn't a perfect sphere.
I've tried using this, but with no luck.
Could anyone point me somewhere or maybe I got this wrong ?
Edit: solved !
So reading carefully throughout Movable Type Scripts I found this (slightly modified for my use):
let getPoint = (distance, bearing, center) => {
let δ = Number(distance) / 6371e3;
let θ = Number(bearing).toRadians();
let φ1 = center[0].toRadians();
let λ1 = center[1].toRadians();
let sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1);
let sinδ = Math.sin(δ), cosδ = Math.cos(δ);
let sinθ = Math.sin(θ), cosθ = Math.cos(θ);
let sinφ2 = sinφ1*cosδ + cosφ1*sinδ*cosθ;
let φ2 = Math.asin(sinφ2);
let y = sinθ * sinδ * cosφ1;
let x = cosδ - sinφ1 * sinφ2;
let λ2 = λ1 + Math.atan2(y, x);
return [φ2.toDegrees(), (λ2.toDegrees()+540)%360-180];
};
It did solved my problem.
You are trying to solve what is known as the first (or direct) geodetic problem. Knowing this name will make your research easier.
As pointed out by the answers to "How to draw polyline perpendicular to another polyline using Leaflet" and "Find destination coordinates given starting coodinates, bearing, and distance", your main options to approach this problem in javascript are cheap-ruler for small(ish) areas and greographiclib for large distances.
cheap-ruler tends to be very fast but inaccurate, and geographiclib tends to be slower but very accurate.
You might find other implementations, each with its own compromises. Geodesy is hard, so there is no "one true way" to calculate distances or azimuths.
Related
There's no simple curved-line tool in turf.js, nor is there an easy way to do it in mapbox (so far as I can see), so I've created a workaround based on this answer in this thread.
However, the curve it creates isn't very smooth or satisfying or has an inconsistent hump based on angle/length.
Ideally, I'd like an arc that is always in a nice, rounded form.
and drawing a line between them. I then offset the midpoint by distance / 5 and apply a bearing. I then connect up the three points with a turf.bezierSpline.
const start = [parseFloat(originAirport.longitude), parseFloat(originAirport.latitude)];
const end = [
parseFloat(destinationAirport.longitude),
parseFloat(destinationAirport.latitude),
];
const distance = turf.distance(start, end, { units: 'miles' });
const midpoint = turf.midpoint(start, end);
const destination = turf.destination(midpoint, distance / 5, 20, { units: 'miles' });
// curvedLine gets rendered to the page
const curvedLine = turf.bezierSpline(
turf.lineString([start, destination.geometry.coordinates, end]),
);
Desired curvature:
Well, that question was created a very long time ago, but I recently encounter this problem.
If anybody is still wondering - this code is good is general, but you've missed one detail. We can't use hardcoded bearing value 20 in turf.destination method, because it's incorrect for most cases. We need our moved midpoint to be right at the middle of our geometry, so we have to find the right angle.
const bearing = turf.bearing(start, end);
Then - if we want our arc to be on the left side of our line, we need to add 90 degrees to our calculated bearing. If on the right side - substract 90 degrees, so:
const leftSideArc = bearing + 90 > 180 ? -180 + (bearing + 90 - 180) : bearing + 90;
NOTE!!! Bearing is value between -180 and 180 degrees. Our value have to be calculated properly in case it'll exceed this range.
And then we can pass our bearing to destination method:
const destination = turf.destination(midpoint, distance / 5, leftSideArc, { units: 'miles' });
Now we have a perfect arc.
I have a flight path with lat/long and elevation and I need to convert this to cartesin X,Y,Z for cesium.js.
I am running into the wall trying to convert this because I don't seem to be getting the right results from my function.
var R = 6371;
function polarToCartesian(latitude, longitude, elevation){
x = (R+elevation) * math.cos(latitude) * math.cos(longitude);
y = (R+elevation) * math.cos(latitude) * math.sin(longitude);
z = (R+elevation) * math.sin(latitude);
var ar = [x,y,z];
return ar;
}
I must not either have the correct formula for polar to cartesian or I don't have the correct radius of earth. I found somewhere that my radius should be 6371 but can't seem to find that same SO question for reference.
I am partialy checking if my code is correct by manually adding up the radius of the earth + altitude of the flight path at a given location and seeing if this equals the length of my x,y,z vector.
For example: x,y,z (3689.2472215653725,3183.2401988117012,13306.90338789763)
is outputted when I give my function this
-93.028,44.6942,7800
lat,long,elevation
Could someone point me to find the right js code to accomplish this conversion?
You should be using Cesium's built-in functions for this. See Cartesian3.fromDegrees and Cartesian3.fromDegreesArray.
For example:
var result = Cesium.Cartesian3.fromDegrees(latitude, longitude, elevation);
Note the result will be as Cesium expects: in meters, not kilometers. This also takes into account the shape of the Ellipsoid, for which the default is WGS84 (the Earth is not a perfect sphere, as your function presumes).
There is nothing wrong with the Yavascript per se. However, your equations are incorrect. You're looking to convert from Lat/Long/Alt to Spherical (aka Cartesian), which was answered here.
So you could rewrite above as:
function polarToCartesian(latitude, longitude, elevation){
const x = math.cos(latitude) * math.cos(longitude) * elevation;
const y = math.cos(latitude) * math.sin(longitude) * elevation;
const z = math.sin(latitude) * elevation;
return [x, y, z];
}
Intro
Hey!
Some weeks ago, I did a small demo for a JS challenge. This demo was displaying a landscape based on a procedurally-generated heightmap. To display it as a 3D surface, I was evaluating the interpolated height of random points (Monte-Carlo rendering) then projecting them.
At that time, I was already aware of some glitches in my method, but I was waiting for the challenge to be over to seek some help. I'm counting on you. :)
Problem
So the main error I get can be seen in the following screenshot:
Screenshot - Interpolation Error? http://code.aldream.net/img/interpolation-error.jpg
As you can see in the center, some points seem like floating above the peninsula, forming like a less-dense relief. It is especially obvious with the sea behind, because of the color difference, even though the problem seems global.
Current method
Surface interpolation
To evaluate the height of each point of the surface, I'm using triangulation + linear interpolation with barycentric coordinates, ie:
I find in which square ABCD my point (x, y) is, with A = (X,Y), B = (X+1, Y), C = (X, Y+1) and D = (X+1, Y+1), X and Y being the truncated value of x, y. (each point is mapped to my heightmap)
I estimate in which triangle - ABD or ACD - my point is, using the condition: isInABD = dx > dy with dx, dy the decimal part of x, y.
I evaluate the height of my point using linear interpolation:
if in ABD, height = h(B) + [h(A) - h(B)] * (1-dx) + [h(D) - h(B)] * dy
if in ACD, height = h(C) + [h(A) - h(C)] * (1-dy) + [h(D) - h(C)] * dx, with h(X) height from the map.
Displaying
To display the point, I just convert (x, y, height) into the world coordinates, project the vertex (using simple perspective projection with yaw and pitch angles). I use a zBuffer I keep updated to check if I draw or not the obtained pixel.
Attempts
My impression is that for some points, I get a wrong interpolated height. I thus tried to search for some errors or some non-covered boundaries cases, in my implementation of the triangulation + linear interpolation. But if there are, I can't spot them.
I use the projection in other demos, so I don't think the problem comes from here. As for the zBuffering, I can't see how it could be related...
I'm running out of luck here... Any hints are most welcome!
Thank for your attention, and have a nice day!
Annexe
JsFiddle - Demo
Here is a jsFiddle http://jsfiddle.net/PWqDL/ of the whole slightly simplified demo, for those who want to tweak around...
JsFiddle - Small test for the interpolation
As I was writing down this question, I got an idea to have a better look at the results of my interpolation. I implemented a simple test in which I use a 2x2 matrix containing some hue values, and I interpolate the intermediate colors before displaying them in the canvas.
Here is the jsFiddle: http://jsfiddle.net/y2K7n/
Alas, the results seem to match the expected behavior for the kind of "triangular" interpolation I'm doing, so I'm definitly running out of ideas.
Code sample
And here is the simplified most-probably-faulty part of my JS code describing my rendering method (but the language doesn't matter much here I think), given a square heightmap "displayHeightMap" of size (dim x dim) for a landscape of size (SIZE x SIZE):
for (k = 0; k < nbMonteCarloPointsByFrame; k++) {
// Random float indices:
var i = Math.random() * (dim-1),
j = Math.random() * (dim-1),
// Integer part (troncated):
iTronc = i|0,
jTronc = j|0,
indTronc = iTronc*dim + jTronc,
// Decimal part:
iDec = i%1,
jDec = j%1,
// Now we want to intrapolate the value of the float point from the surrounding points of our map. So we want to find in which triangle is our point to evaluate the weighted average of the 3 corresponding points.
// We already know that our point is in the square defined by the map points (iTronc, jTronc), (iTronc+1, jTronc), (iTronc, jTronc+1), (iTronc+1, jTronc+1).
// If we split this square into two rectangle using the diagonale [(iTronc, jTronc), (iTronc+1, jTronc+1)], we can deduce in which triangle is our point with the following condition:
whichTriangle = iDec < jDec, // ie "are we above or under the line j = jTronc + distanceBetweenLandscapePoints - (i-iTronc)"
indThirdPointOfTriangle = indTronc +dim*whichTriangle +1-whichTriangle, // Top-right point of the square or bottm left, depending on which triangle we are in.
// Intrapolating the point's height:
deltaHeight1 = (displayHeightMap[indTronc] - displayHeightMap[indThirdPointOfTriangle]),
deltaHeight2 = (displayHeightMap[indTronc+dim+1] - displayHeightMap[indThirdPointOfTriangle]),
height = displayHeightMap[indThirdPointOfTriangle] + deltaHeight1 * (1-(whichTriangle? jDec:iDec)) + deltaHeight2 * (!whichTriangle? jDec:iDec),
posX = i*distanceBetweenLandscapePoints - SIZE/2,
posY = j*distanceBetweenLandscapePoints - SIZE/2,
posZ = height - WATER_LVL;
// 3D Projection:
var temp1 = cosYaw*(posY - camPosY) - sinYaw*(posX - camPosX),
temp2 = posZ - camPosZ,
dX = (sinYaw*(posY - camPosY) + cosYaw*(posX - camPosX)),
dY = sinPitch*temp2 + cosPitch*temp1,
dZ = cosPitch*temp2 - sinPitch*temp1,
pixelY = dY / dZ * minDim + canvasHeight,
pixelX = dX / dZ * minDim + canvasWidth,
canvasInd = pixelY * canvasWidth*2 + pixelX;
if (!zBuffer[canvasInd] || (dZ < zBuffer[canvasInd])) { // We check if what we want to draw will be visible or behind another element. If it will be visible (for now), we draw it and update the zBuffer:
zBuffer[canvasInd] = dZ;
// Color:
a.fillStyle = a.strokeStyle = EvaluateColor(displayHeightMap, indTronc); // Personal tweaking.
a.fillRect(pixelX, pixelY, 1, 1);
}
}
Got it. And it was as stupid a mistake as expected: I was reinitializing my zBuffer each frame...
Usually it's what you should do, but in my case, each frame (ie call of my Painting() function) adds details to the same frame (ie drawed static scene from a constant given point of view).
If I reset my zBuffer at each call of Painting(), I lose the depth information of the points drawn during the previous calls. The corresponding pixels are thus considered as blank, and will be re-painted for any projected points, without any regard for their depth.
Note: Without reinitiliazation, the zBuffer gets quite big. Another fix I should have done earlier was thus to convert the pixel's positions of the projected point (and thus the indices of the zBuffer) into integer values:
pixelY = dY / dZ * minDim + canvasHeight +.5|0,
pixelX = dX / dZ * minDim + canvasWidth +.5|0,
canvasInd = pixelY * canvasWidth*2 + pixelX;
if (dZ > 0 && (!zBuffer[canvasInd] || (dZ < zBuffer[canvasInd]))) {
// We draw the point and update the zBuffer.
}
Fun fact
If the glitches appeared more obvious for relief with the sea behind, it wasn't only for the color difference, but because the hilly parts of the landscape need much more points to be rendered than flat areas (like the sea), given their stretched surface.
My simplistic Monte-Carlo sampling of points doesn't take this characteristic into account, which means that at each call of Painting(), the sea gains statistically more density than the lands.
Because of the reinitialization of the zBuffer each frame, the sea was thus "winning the fight" in the picture's areas where mountains should have covered it (explaining the "ghostly mountains" effect there).
Corrected JsFiddle
Corrected version for those interested: http://jsfiddle.net/W997s/1/
im trying to see if this
Math.sqrt(
Math.pow((position.coords.latitude -45),2) +
Math.pow((position.coords.longitude-75),2)
)*79;
Matches this:
Distance to store (km) = Square Root (
(Current Latitude – 45)^2 +
(Current Longitude ‐75)^2
) *79
Right now im getting 11,XXX KM which is way to much, but I don't see any mistakes.
I also tried doing it like this:
var x = Math.pow((position.coords.latitude-45),2);
var y = Math.pow((position.coords.longitude-75),2);
var z = Math.sqrt(x+y);
var zz = z*79;
but it gave me the same answer.
This formula is valid for short distances to the point of latitude 45 and longitude 75.
What you want, as you're not located in Kazakstan, is the distance to (45, -75), that is
Math.sqrt(Math.pow((lat-45),2)+Math.pow((lon+75),2))*79;
^
Note that it's only as precise as your coordinates are precise and that for bigger distances you should grab a better formula.
I have a bit of a problem. I am trying to do the following using Javascript & the Google Maps API v2:
I can draw individual circles just fine using formulas found all over the Internet. The problem I am facing is that the circles must:
A. Be concentric, and
B. Must have different radius for each "quadrant", i.e., NE, NW, SE & SW
I've searched almost everywhere I can think of on the Internet, and have come up with no way on how to do this. Clearly someone has done this before, and thus why I'm asking in a forum of programmers. :)
Thanks!
UPDATE: I have drawn out, using the following code, what I think the coordinates for each of the points would be. for the drawing below:
This was obtained using the following JS:
http://gist.github.com/181290
NOTE: This javascript is from (slightly modified) the following site, which may hold more answers in terms of what the algorithm may end up being: http://www.movable-type.co.uk/scripts/latlong.html
UPDATE 2: I was able to get this in Google Maps:
Created using the following code:
var NEQ = [0, 90];
var SEQ = [90, 180];
var SWQ = [180, 270];
var NWQ = [270, 0];
// var centrePoint = new LatLon(25.0, -83.1);
// pointsForWindQuadrant(NEQ, centrePoint, 50);
function pointsForWindQuadrant(quadrantDegrees, centrePoint, radius){
var points = [];
// Points must be pushed into the array in order
points.push(new google.maps.LatLng(centrePoint.lat, centrePoint.lon));
for(i = quadrantDegrees[0]; i <= quadrantDegrees[1]; i++){
var point = centrePoint.destPoint(i, radius * 1.85);
points.push(new google.maps.LatLng(point.lat, point.lon)); // Radius should be in nautical miles from NHC
}
points.push(new google.maps.LatLng(centrePoint.lat, centrePoint.lon));
return points;
}
UPDATE 3: I should probably also point out that this is for a geographic coordinate system (as this whole thing is for tropical cyclone wind radii), not the Cartesian coordinate system. Thanks!
Basically, calculate the circle as the x,y = (cos(a), sin(a)), and then multiple this (both terms) by a radius that's the appropriate function of the angle. I don't know Javascript well, or Google maps, so I'll do this in Python, hopefully it's clear enough from this.
from pylab import *
def Rscale(a):
if a>3*pi/2: # lower right, and then work CW around the circle
return 1.
elif a>pi: # lower left
return .9
elif a>pi/2: # upper left
return .8
else: # upper right
return 1.
def step_circle(R):
return array([(R*Rscale(a))*array([cos(a), sin(a)]) for a in arange(0, 2*pi, .001)])
for R in (.5, .7, .9): # make three concentric circles
c = step_circle(R)
plot(c[:,0], c[:,1])
show()
Which gives
I couldn't really follow your sketch, so I just guessed at the numbers. Also, I made the two rightmost quadrants to be the same since that's what your plot looked like, but that is, of course, optional.
I figured it out. Here is the final code. Maybe it can be refactored a bit?
// Returns points for a wind field for a cyclone. Requires
// a LatLon centre point, and an array of wind radii, starting
// from the northeast quadrant (NEQ), i.e., [200, 200, 150, 175]
//
// Returns points to be used in a GPolyline object.
function pointsForWindQuadrant(centrePoint, radii){
if(radii.length != 4){ return false; }
var points = [];
var angles = [0, 90, 180, 270];
// For each angle 0, 90, 180, 270...
for(a = 0; a < angles.length; a++){
// For each individual angle within the range, create a point...
for(i = angles[a]; i <= angles[a] + 90; i++){
var point = centrePoint.destPoint(i, radii[a] * 1.85); // Radius should be in nautical miles from NHC
points.push(new google.maps.LatLng(point.lat, point.lon));
}
}
// Add the first point again, to be able to close the GPolyline
var point = centrePoint.destPoint(0, radii[0] * 1.85);
points.push(new google.maps.LatLng(point.lat, point.lon));
return points;
}
This results in the following: