Viewing points on a sphere from an external perspective

69 Views Asked by At

I have an application that generates points on edge of a sphere. They're recorded as Lat/Lon coordinates in degrees or radians (example is degrees).

I am unable to figure out how to display them on a cartesian plane as viewed from a point external to the sphere (imagine a satellite photographing earth).

In the example below I have 8 points (2 sets of four points at different latitudes encompassing the sphere). I have manually plotted them roughly as you would see these from two angles - one above the 'north pole' and one above a point on the 'equator'.

In this example the top down view would see all 8 points cleanly but the side view would have overlapping points assuming the sphere is transparent.

I'm not 100% sure but I suspect the 'height' of viewing agent above the sphere is relevant so having that as a variable would seem valid.

I expect the function would be something like this:

function translateSpherePoint(sphereRadius, pointLat, pointLon, viewX, viewY, viewZ){
    //magic code
    return {newPointX, newPointY}
}

I've been studying this at length: https://www.movable-type.co.uk/scripts/latlong.html and this has proven very useful but is primarily for point manipulation on the sphere - not so much for viewing.

var canvas = document.getElementById("mainCanvas");
var ctx = canvas.getContext('2d');
canvas.width = 500; canvas.height = 400; ctx.font = "20px Georgia";
ctx.fillText("Top down view", 25, 50);
ctx.fillText("Side view", 25+250, 50);

let points = [
  //points half way between the equator and north pole
  {point: 1, latDeg:45, lonDeg:45},
  {point: 2, latDeg:45, lonDeg:135},
  {point: 3, latDeg:45, lonDeg:-135},
  {point: 4, latDeg:45, lonDeg:-45},

  //points 3/4 of the way between the equator and the north pole
  {point: 5, latDeg:67.5, lonDeg:45},
  {point: 6, latDeg:67.5, lonDeg:135},
  {point: 7, latDeg:67.5, lonDeg:-135},
  {point: 8, latDeg:67.5, lonDeg:-45}
]

ctx.font = "13px Georgia";
//Top Down View
ctx.beginPath();
ctx.arc(100,200,100,0,2*Math.PI);
ctx.stroke();ctx.beginPath();
ctx.arc(100-50,200-50,10,0,2*Math.PI); ctx.fillText("1", 100-50-4, 200-50+3);
ctx.stroke();ctx.beginPath();
ctx.arc(100+50,200-50,10,0,2*Math.PI); ctx.fillText("2", 100+50-4, 200-50+3);
ctx.stroke();ctx.beginPath();
ctx.arc(100+50,200+50,10,0,2*Math.PI); ctx.fillText("3", 100+50-4, 200+50+3);
ctx.stroke();ctx.beginPath();
ctx.arc(100-50,200+50,10,0,2*Math.PI); ctx.fillText("4", 100-50-4, 200+50+3);
ctx.stroke();ctx.beginPath();

ctx.arc(100-25,200-25,10,0,2*Math.PI); ctx.fillText("5", 100-25-4, 200-25+3);
ctx.stroke();ctx.beginPath();
ctx.arc(100+25,200-25,10,0,2*Math.PI); ctx.fillText("6", 100+25-4, 200-25+3);
ctx.stroke();ctx.beginPath();
ctx.arc(100+25,200+25,10,0,2*Math.PI); ctx.fillText("7", 100+25-4, 200+25+3);
ctx.stroke();ctx.beginPath();
ctx.arc(100-25,200+25,10,0,2*Math.PI); ctx.fillText("8", 100-25-4, 200+25+3);
ctx.stroke();ctx.beginPath();

//Side View
ctx.beginPath();
ctx.arc(350,200,100,0,2*Math.PI);
ctx.stroke();ctx.beginPath();
ctx.arc(350-50,200-50,10,0,2*Math.PI); ctx.fillText("1/4", 350-50-10, 200-50+3);
ctx.stroke();ctx.beginPath();
ctx.arc(350+50,200-50,10,0,2*Math.PI); ctx.fillText("2/3", 350+50-10, 200-50+3);
ctx.stroke();ctx.beginPath();
ctx.arc(350-25,200-75,10,0,2*Math.PI); ctx.fillText("5/8", 350-25-10, 200-75+3);
ctx.stroke();ctx.beginPath();
ctx.arc(350+25,200-75,10,0,2*Math.PI); ctx.fillText("6/7", 350+25-10, 200-75+3);
ctx.stroke();ctx.beginPath();
<canvas id="mainCanvas"> </canvas>

1

There are 1 best solutions below

1
On

My theory is to use the new viewpoint to translate a new cartesian plane and return the xy values relative to that new plane. An example is in my graph

//(sphereRadius, pointLat, pointLon, viewX, viewY, viewZ)
//i assume that into the screen is negative; out of the screen is positive
//this example is a demonstration of the graph link
function translateSpherePoint(r,px,py,pz,vx,vy,vz){
  var curZ=r*2; var curY=0; var curX=0 //there must be a beginning viewpoint to translate from
  function distance2d(x1,y1,x2,y2){
    return((x2-x1)**2+(y2-y1)**2)**0.5
  }
  function distance3d(x1,y1,z1,x2,y2,z2){
    return((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)**0.5
  }
  function intersect(m1,c1,m2,c2){
    var x=(c2+(c1*-1))/(m1+(m2*-1))
    var y=c1+(m1*x); return[x,y]
  }
  //now for the tricky parts
  var m2=vy/vx; var m1=1/-m2;
  var newOrigin=intersect(m2,0,m1,-1*(px*m1))
  newOrigin.push(pz*(px/newOrigin[0])) //applying the z axis since a straight line is proportional and i made these lines that they MUST connect(shortcut for a 3d intersect since i know they will).. also, this part is not close to perfect but gets things similar to the example correctly :{
  var newPointX=NaN
  var newPointY=NaN
  var newPointZ=-distance3d(px,py,pz,vx,vy,vz)
  if(-newPointZ>distance3d(vx,vy,vz,0,0,0)){return(null)} //this is BEHIND your view of the sphere if this condition is true
  return [newPointX, newPointY, newPointZ, newOrigin]
}

var arr=translateSpherePoint(1,1,0,0,1,2,0)
console.log(arr)
console.log(`I have yet to determine x and y, so it looks like ${arr.filter((a,i)=>i<3)}`)