Mapping latitude and longitude to Canvas points in JavaScript - javascript

I'm writing an AR application in which the user sees a ship from the perspective of the captain of the ship like this
I want to use coordinates of other ships (like the one on the left) to draw the ships on top of the image using Canvas. The problem I'm having is mapping the latitude, longitude to Canvas points.
I've read about the Haversine and Bearing formulas but I can't figure out how to use them to do what I want. I know the latitude and longitude of the ship the user is looking from and tried using them to calculate the Canvas points of the other ship, but I can't get it to work. Any ideas?

Finding real world object in camera's view.
This is the most simplistic answer possible, as there are many unknowns that will affect the result.
The problem
The image illustrates the problem as I understand it.
We have 3 ships (the minimum required to find a solution) marked with a red A,B,C. We know the latitude and longitude of each ship. As they are close there is no need to correct for any distortion due to converging longitudes.
The green lines represent the camera's view, the image is projected via a lens onto the CCD (green line above A) The twp gray lines then are projected onto the camera's screen below the ship.
Triangulation and triangles.
From the longitudes we can find the distance from each ship to the other, giving us the lengths of the sides of a triangle.
var ships = {
A : { lat : ?, long : ? },
B : { lat : ?, long : ? },
C : { lat : ?, long : ? },
}
var AB = Math.hypot(ships.A.lat - ships.B.lat, ships.A.long - ships.B.long);
var BC = Math.hypot(ships.C.lat - ships.B.lat, ships.C.long - ships.B.long);
var CA = Math.hypot(ships.A.lat - ships.C.lat, ships.A.long - ships.C.long);
The inset triangle shows how to find the angle of a corner given the lengths of the sides.
For this we are after the angle pheta
var pheta = Math.acos(BC * BC - AB * AB + CA * CA) / (-2 * AB * CA));
The camera's field of view.
Now we need an extra bit of information. That is the field of view of the camera (in image as the green lines and red FOV). You will have to find those details for the camera you are using.
A typical phone camera has a focal length (fl) from 24-35mm equivalent and knowing the CCD size in relative terms you can find the FOV with var FOV = 2 * Math.atan((CCD.width / 2) / fl) But this is problematic as a modern phone is under 10mm thick and the CCD is tiny, what are the actual dimensions?
There is a long and complicated process you can use to determine the FOV without knowing the internal dimensions of the camera, but easy to look up the phone specs.
For now let's assume the camera has a FOV of 67deg (1.17radians). If the camera has a resolution of 1280 and we ignore lens distortions, and keep the camera vertical and at the same level as the targets , we can calculate the distance in pixels between the two ships via the angle between them.
var FOV = 1.17; // radians the Field of View of the camera
var pixels = (1.17 / 1280) * pheta; // 1280 is horizontal resolution of display
So now we have the distance in pixels between the two ships on the camera. Assuming that they fit on the camera we are missing one more important bit of information.
Bearings
We need to know which way the camera is pointing as a bearing. Only when we have that can we find the ships. So lets assume the GPS on the phone gives you the live bearing. What we need is the bearing to one of the ships.
I had to dig deep into my archives to find this, It had no source or referance so can only provide as is. All it had is Haversin so assuming that is the method used.
After some further digging I found a referance to what is likely the original source code that this was derived from Calculate distance, bearing and more between Latitude/Longitude points
function deg2rad(angle) { return angle * 0.017453292519943295 }
function rad2deg(angle) { return angle / 0.017453292519943295 }
//return bearing in radians.
function getBearing(lat1,lon1,lat2,lon2){
var earth = 6371e3;
var lat1 = lat1.toRadians();
var lat2 = lat2.toRadians();
var lon1 = lon1.toRadians();
var lon2 = lon2.toRadians();
var latD = lat2-lat1;
var lonD = lon2-lon1;
var a = Math.sin(latD / 2) * Math.sin(latD / 2) + Math.cos(lat1 ) * Math.cos(lat2) * Math.sin(lonD / 2) * Math.sin(lonD / 2);
var c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a) );
return earth * c;
}
So now you can get the bearing from you to one of the ships. Use that and your bearing to find the difference in angle between you and it.
var yourBearing = ?; // in radians
var shipBBearing = getBearing(ships.A.lat, ships.A.long, ships.B.lat, ships.B.long);
Now get the differance in angle
var shipBAt = yourBearing - shipBBearing
Knowing the pixel FOV
var shipBPixelsFromCenter = (FOV / 1280) * shipBAt;
var shipBXpos = 1280 / 2 - shipBPixelsFromCenter;
// and from above the dif in pixels between ships is
var shipCXpos = shipBXpos + pixels.
And done.

Related

How to create a square polygon with knowing single coordinate, angle and distance?

How can I create a polygon(square) with only knowing coordinate of single point with distance of each side (3km) and angle which is 90 degree using javascript. if I will have the coordinate of all 4 point then i can draw a rectangle.. Any suggestion will be of great help.
Mathematically, you cannot draw a square on a sphere, but we'll assume that:
the distance is small enough compared to the Earth that it doesn't matter and we can approximate a flat surface
it's not in the polar regions, as that messes with the maths.
From what I understood of your question, your start with Point 1 and the angle alpha which is the direction to the north (heading). If you define your angle differently, you'll have to adapt.
So we start with the following (I put the angle at 30 so it's more visible):
The first point is defined by lat1,lng1
var alpha = 30;
var dist=3;
var Rearth=6371.0
let cosa=Math.cos(alpha* Math.PI/180);
let sina=Math.sin(alpha* Math.PI/180);
let dlng=Rearth*Math.cos(lat1* Math.PI/180)
(dlng is the correction factor as the longitude dimensions gets smaller as the latitude goes from the equator to the poles. As the square is small, we can assume that the difference of latitude between the sides of the square doesn't really matter)
and to get to point 2 (as the angle between the centre of the earth and the side of the square is very small, there's no need to add a asin() function, as beta ~ sin(beta) in radians )
let lat2 = lat1 + (dist*cosa/Rearth*180/Math.PI);
let lng2 = lng1 + (dist*sina/(dlng)*180/Math.PI);
then we can find points 3 and 4 in a similar way:
let lat3 = lat2 - (dist*sina/Rearth*180/Math.PI);
let lng3 = lng2 + (dist*cosa/(dlng)*180/Math.PI);
let lat4=lat1 - dist*sina/Rearth*180/Math.PI;
let lng4=lng1 + dist*cosa /(dlng)*180/Math.PI;
and then you can use the lat,lng pairs to create a polygon:
var latlngs = [[lat1, lng1],[lat2, lng2],[lat3,lng3],[lat43,lng43],[lat4,lng4]];
L.polygon(latlngs, {color: 'blue'}).addTo(map);
For example, with dist=16km and alpha=45, you can draw a square that fits over the boundaries of Washington DC:

Three.js position objects on curve - rotate towards center of circle

I'm attempting to create a snazzy VR menu so that when the user looks down the menu items are in a column below camera curved around circle so they look the same and are rotated towards camera.
Here is my attempt thus far
And a CodePen with the example
http://codepen.io/bknill/pen/BLOwLj?editors=0010
I'm using some code I found that calculates the position
var radius = 60; // radius of the circle
var height = 60,
angle = 0,
step = (Math.PI /2 ) / menuItems.length;
menuItems.forEach(function(item,index){
var menuItem = createMenuItem(item.title);
menuItem.position.y = - Math.round(height/2 + radius * Math.sin(angle));
menuItem.position.z = Math.round(height/2 + radius * Math.sin(angle));
// menuItem.rotation.x = -Math.round(Math.PI * Math.sin(angle));
angle += step;
menu.add(menuItem);
})
Which is almost right, the next stage is to get them to rotate in a uniform way towards the camera. Using menuItem.lookAt(camera.position) isn't working - they're not uniform rotation.
child.lookAt(camera.position.normalize()) does this
Anyone let me know the clever maths I need to get the rotation of the item so they face the camera and look like they're on a curve?
The simplest way to have an object facing another object is using lookAt.
Be aware this method needs a direction vector, not the point where you want it to look at.
(position you want to look at - position of your object).normalize();
In your case:
var dirToLookAt = new THREE.Vector3();
dirToLookAt.subVectors(menuItem.position, camera.position);
// could be: dirToLookAt.subVectors(camera.position, menuItem.position);
dirToLookAt.normalize();
Operations based on Vector3 documentation.
For more info you can read the last discussion I had about this method.

Calculate radius of helix so that models in helix are inside the frustum

I'm building an app in which I present some planes with textures. However, I would like to calculate the radius of the helix (which I use in my calculations to create a helix), dynamically based on the frustum width and the camera position.
The helix is positioned at the center of the screen x=0, y=0, z=0.
I would like this to take under consideration the screen orientation (landscape/ portrait).So far this is the code I have but it seems that I'm missing something because the planes at the left and the right are not inside the viewport.
App.prototype.calculateHelixRadius = function(){
// plane width = height = 512;
var friend = this.getFriend();
var vFOV = friend.camera.fov * Math.PI / 180;
var dist = utils.getAbsPointsDistance3D(friend.camera.position, friend.scene.position);
var aspect = friend.settings.container.clientWidth / friend.settings.container.clientHeight;
var frustumHeight = 2.0 * dist * Math.tan(0.5 * vFOV);
var frustumWidth = frustumHeight * aspect;
return utils.isLandscape() ? frustumHeight / 2 : frustumWidth / 2 ;
};
What am I doing wrong and why are the planes at the edges of the screen not inside?
Also for reference here is the code of getAbsPointsDistance3D
var utils = {
// other helpers...
getAbsPointsDistance3D: function(p1, p2) {
var xd = p2.x - p1.x;
var yd = p2.y - p1.y;
var zd = p2.z - p1.z;
return Math.sqrt(xd * xd + yd * yd + zd * zd);
}
};
update
I tried decreasing the dist parameter but the results are not consistent...
I wonder if the following explains your clipping.
You calculate your frustum characteristics, then calculate the helix radius using, say, the frustum width (width or height depending on the screen aspect...I may be getting some of the particulars wrong here because your question does not completely explain the details, but the general concepts still hold). The image below is a top view of the scenario which shows a circle representing the cylinder that encloses the helix. I believe you have calculated radius1. If so, note that there will be clipping of the cylinder (the shaded area), and thus the helix, in "front" of the cylinder centre.
Instead you need to calculate the cylinder/helix radius as shown in the second image, i.e. you need radius2. If the large angle at the image left is fov (again, vFOV? or hFOV?, etc., depending on whether your helix is going up-down or side-to-side, etc.), then its half angle is fov/2. This is the same angle shown in the centre of the cylinder. Thus, you need to decrease your helix radius as follows: radius2 = radius1 * cos(fov/2).

Javascript - calculate distance between latlng as percentage

I have a google map. Within the map I have a bounding box. Within that box, I have a series of points. I need a function to express the position of the point with respect to the bounds - ie it is 25% from the top of the box, 15% from the left.
I've tried countless (been at it all day) different formulae, with no result.
I thought this would work (pseudo code):
var y = ((point.latitude - ne.latitude) / (sw.latitude - ne.latitude)) * boundingboxheight
Normally you would have to convert degrees to radians and convert this to miles or km using radius of earth at equator to do distance calculations.
But in this case you can just use the degrees as they are. Using the following values pointLat is 25% below neLat.
var neLat = 62;
var swLat = 58;
var pointLat = 61;
var y = ((neLat - pointLat)/(neLat - swLat))*100;

googlemaps get bounding box around marker

In googlemaps api v2 I have one marker on map and i need to calculate a bounding box around this one. How would I get a bonding box of 5 by 5 kilometers of which this marker is the center?
I'm not sure that such a functionality is provided by google map, but math will help you to survive ;) Calculate distance, bearing and more between Latitude/Longitude points is a great reference to different calculations with geographic data. Open that page, and go to "Destination point given distance and bearing from start point" part, there are formulas, as well as online calculator, so you can check them (as well as you can see points on the map). Formula has few parameters:
(lat1,lng1) - your point (marker coordinates)
d - distance (in your case it would be 2.5km)
brng - angle..
to find bound you need to find coordinates of south, north, east and west rectangle sides, so everything you will change in parameters is angle, and in your case it will be 0, 90, 180 and 270 grads. Formulas:
var lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
Well, specifying angle = 0 you find north, PI/2 - east, PI - south, 3*PI/2 - west (angles should be passed in radians).
R = earth’s radius (mean radius = 6,371km)
ADD-ON: just looked at the source code of that page, because when I enter in online form bearing = 0, distance = 2 then I see map with two points and according to the map scale the distance between them is really 2km. Well, you can use this library under a simple attribution license, without any warranty express or implied, just include it
<script src="http://www.movable-type.co.uk/scripts/latlon.js"></script>
Function you need is:
/**
* Returns the destination point from this point having travelled the given distance (in km) on the
* given initial bearing (bearing may vary before destination is reached)
*
* see http://williams.best.vwh.net/avform.htm#LL
*
* #param {Number} brng: Initial bearing in degrees
* #param {Number} dist: Distance in km
* #returns {LatLon} Destination point
*/
LatLon.prototype.destinationPoint = function(brng, dist) {
dist = typeof(dist)=='number' ? dist : typeof(dist)=='string' && dist.trim()!='' ? +dist : NaN;
dist = dist/this._radius; // convert dist to angular distance in radians
brng = brng.toRad(); //
var lat1 = this._lat.toRad(), lon1 = this._lon.toRad();
var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) +
Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) );
var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1),
Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2));
lon2 = (lon2+3*Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180
return new LatLon(lat2.toDeg(), lon2.toDeg());
}
and when I enter in online form bearing = 0, distance = 2, this method is executed with arguments latLonCalc.destinationPoint( 0, "2" );
try it, otherwise give me input parameters so I could check what's wrong
UPDATE2 that library works with grads,and converts them to radians for calculations and then back to grads. Just performed simple test:
var latLonCalc = new new LatLon( 25, 45 );
var point = latLonCalc.destinationPoint( 0, "2" );
console.info( point );
// prints 25°01′05″N, 045°00′00″E { _lat=25.01798643211838, _lon=45.00000000000005, _radius=6371}
so the distance between entry point and final destination is a bit more than 1 minute;
earth = 2 * PI * 6371; // 40 009.98km
=> 40 009.98km / 360grad =~111.14
=> 111,14 / 60 = 1.85 (km/minute) ~2km
it was round calculation, which tells me that final point is not far away then entry point, and distance should be 2km ;)
Not the real one you required but check this article
http://www.svennerberg.com/2008/11/bounding-box-in-google-maps/

Categories