Kiwi
Kiwi

Reputation: 9

Get latitude longitude on click sphere with threejs

I am here today because I would like your help. I'm trying to create the earth globe with lat and long, but I can't get the geographic coordinates.

This is my code:

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = 5;
scene.add(camera);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
renderer.render(scene, camera);

function anime() {
  renderer.render(scene, camera);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  renderer.setSize(window.innerWidth, window.innerHeight);
  requestAnimationFrame(anime);
}

anime();

class Earth {
  constructor(radius, scene, camera, renderer) {
    this.scene = scene;
    this.radius = radius;
    this.camera = camera;
    this.renderer = renderer;
    this.raycaster = new THREE.Raycaster();
    this.mouse = new THREE.Vector2();
    this.geometry = new THREE.SphereGeometry(this.radius, 32, 32);
    this.texture = new THREE.TextureLoader().load('https://i.ibb.co/4KGwCLD/earth-atmos-2048.jpg');
    this.material = new THREE.MeshBasicMaterial({
      color: 0xffffff,
      map: this.texture,
    });
    this.mesh = new THREE.Mesh(this.geometry, this.material);
    this.obj = new THREE.Object3D();
  }

  initObject() {
    // this.scene.add(this.scene);
    this.obj.add(this.mesh);
    this.obj.name = 'earth';
    this.scene.add(this.obj);
  }

  onClick(event) {
    event.preventDefault();

    let canvas = this.renderer.domElement;
    let vector = new THREE.Vector3(
      ((event.offsetX) / canvas.width) * 2 - 1, -((event.offsetY) / canvas.height) * 2 + 1,
      0.5);
    vector.unproject(this.camera);
    this.raycaster.set(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = this.raycaster.intersectObjects(this.scene.children);
    if (intersects.length > 0) {
      console.log(intersects[0].point.x);
      console.log(intersects[0].point.y);
      console.log(intersects[0].point.z);
    }
  }

  earthRotate() {
    this.obj.rotation.y += 0.01;
    requestAnimationFrame(this.earthRotate.bind(this));
  }
}

const earth = new Earth(3, scene, camera, renderer);
earth.initObject();

addEventListener("click", (event) => {
  earth.onClick(event);
});
/* earth.earthRotate(); */
body {
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.min.js" integrity="sha512-0tlhMhMGPohLm/YwaskxH7jJuUGqU/XPTl+HE0dWrhGbpEBRIZYMQdbHC0CmyNPzZKTBd8JoVZnvMcL7hzlFOg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r99/three.js" integrity="sha512-RixpleQfVXfhJUrmXlxWwitZGvMWk13+KhCsaYdeod5xryBN6gqo3RJ9xvaHn8VUeNuUnYfvzKBhLBnJnpEsgA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Is it possible to have geographic coordinates on a sphere with ThreeJS. If so do you know how to do it?

Thank you in advance for your help !

Upvotes: 0

Views: 876

Answers (1)

TheJim01
TheJim01

Reputation: 8886

Intersections are returned in world coordinates, so if you click the same place over and over as your globe is spinning, you will get (about) the same hit position every time.

THAT SAID, these hit points are based on the geometry of the sphere, so a low-poly sphere could actually return a position that does not match a lat/lon position as if the sphere were perfect.

To get a better result, you'll need to take a couple more steps.

1. Create a sphere representation

Use a Sphere to create a mathematically perfect representation of your globe.

2. Use Ray to find the intersection point

The Three Raycaster is a great tool for easily setting up raycasting. But you can use Raycaster.ray (Ray) to perform manual steps, too.

For example, use Ray.intersectSphere against your perfect sphere to get the "real" intersection point.

3. Convert to local coordinates

You were getting the same(-ish) click point because the intersection is in world coordinates. To convert to local coordinates, use the very convenient Object3D.worldToLocal to transform the returned world hit point into a local hit point (the Vector3 is changed using this method, so keep this in mind!).

4. Get your lat/lon

Pick a point on your globe which will represent the intersection point of your equator and prime meridian (0, 0). For a sphere where north is +Y, your origin could be new Vector3( 0, 0, sphere.radius )

Assuming you rotate about the Y axis...

For longitude, remove the Y component of your hit vector (hit.y = 0), set the length of the vector to your sphere radius (hit.setLength( sphere.radius )), then use Vector3.angleTo to find the angle (in radians) between the hit vector and and your "zero" vector, and convert to degrees. Use the X component to determine the East/West component of your value.

For latitude, create a copy of your hit vector. Remove the Y component from the copy and set its length to the sphere's radius, just like for longitude. But instead of comparing to the origin vector, use angleTo to get the angle between the copy and the hit vectors. Convert to degrees. Use the Y value to determine the North/South component of your value.

It's possible to use normalized vectors to perform these comparisons instead, but I think it's easier to visualize if you picture the vectors representing points on the surface of your sphere.

Upvotes: 1

Related Questions