It would be great if users could hover over the small spheres in different countries to get more information.
Initially, I thought using a raycaster would help achieve this, but it's not responding to mouse movements as expected. It seems like the issue might be related to the camera rotation.
This code snippet is crucial:
d3.timer( function ( t ) {
theta += 0.1;
camera.position.x = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
camera.position.y = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
//camera.position.z = 100 * Math.cos( THREE.MathUtils.degToRad( theta ) );
camera.lookAt( scene.position );
camera.updateMatrixWorld();
controls.update();
raycaster.setFromCamera( pointer, camera );
const intersects = raycaster.intersectObjects( earthPivot.children, false );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
console.log(intersects[0].object.name)
INTERSECTED = intersects[ 0 ].object;
}
} else {
INTERSECTED = null;
}
renderer.render( scene, camera );
} );
Please review this particular piece of code.
<div id='container'> </div>
<script src="https://d3js.org/d3.v6.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://unpkg.com/d3-array@1"></script>
<script src="https://unpkg.com/d3-collection@1"></script>
<script src="https://unpkg.com/d3-dispatch@1"></script>
<script src="https://unpkg.com/d3-request@1"></script>
<script src="https://unpkg.com/d3-timer@1"></script>
<script type='module'>
import * as THREE from "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4d39255f28280d7d637c7f7a637d">[email protected]</a>/build/three.module.js";
import { OrbitControls } from "https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6a1e02180f0f2a5a445b58257d78407b79727676737c79090c12404852530c015153"><span class="__cf_email__" data-cfemail="96f2ee64f3f39069aaaa8b868db289d6feffdddcfaa9fd01fcfefdfdfd0077fbfedc6bffbffebebec07548494a3e52efefe10cd0dededece"></span>[email protected]</a>/examples/jsm/controls/OrbitControls.js";
var width = 650;
var height = 650;
var radius = 168,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera( 100, width / height, 1, 1000 ),
renderer = new THREE.WebGLRenderer( { alpha: true } ),
container = document.getElementById( 'container' ),
controls,
raycaster;
const pointer = new THREE.Vector2();
let INTERSECTED;
raycaster = new THREE.Raycaster();
container.addEventListener( 'mousemove', pointerMove );
let theta = 0;
scene.background = new THREE.Color( "rgb(20,20,20)" );
camera.position.set( 0, 0, 300 );
camera.lookAt( new THREE.Vector3() );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( width, height );
container.appendChild( renderer.domElement );
let earthPivot = new THREE.Group();
d3.json( "https://raw.githubusercontent.com/alessiogmonti/alessiogmonti.github.io/master/Pages/Index/dataFranceModified.json", function ( error, topology ) {
if ( error ) throw error;
/// NOT RELEVANT ////////////////////////////////////////////
var countries = [];
var cones = [];
for ( var i = 0; i < topology.objects.countries.geometries.length; i ++ ) {
var rgb = [];
var newcolor;
for ( var j = 0; j < 3; j ++ ) {
rgb.push( Math.floor( Math.random() * 255 ) );
newcolor = 'rgb(' + rgb.join( ',' ) + ')';
}
var mesh = wireframe( topojson.mesh( topology, topology.objects.countries.geometries[ i ] ), new THREE.LineBasicMaterial( { color: newcolor, linewidth: 5 } ) );
countries.push( mesh );
scene.add( mesh );
mesh.geometry.computeBoundingBox();
var center = new THREE.Vector3();
mesh.geometry.boundingBox.getCenter( center );
mesh.add( earthPivot );
let height = 1;
const geometry = new THREE.SphereGeometry( height, 50, 36 );
const material = new THREE.MeshBasicMaterial( { color: newcolor } );
const cone = new THREE.Mesh( geometry, material );
cone.position.copy( center );
cone.position.setLength( radius + height );
cone.name = topology.objects.countries.geometries[ i ].properties[ 'name' ];
cones.push( cone );
earthPivot.add( cone );
/// NOT RELEVANT //////////////////////////////////////////////////////////////
}
controls = new OrbitControls( camera, renderer.domElement );
const helper = new THREE.CameraHelper( camera );
scene.add( helper );
const axesHelper = new THREE.AxesHelper( 50 );
scene.add( axesHelper );
d3.timer( function ( t ) {
theta += 0.1;
camera.position.x = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
camera.position.y = 100 * Math.sin( THREE.MathUtils.degToRad( theta ) );
//camera.position.z = 100 * Math.cos( THREE.MathUtils.degToRad( theta ) );
camera.lookAt( new THREE.Vector3() );
camera.updateMatrixWorld();
controls.update();
// pointer-camera interaction
// const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
// const geometry = new THREE.BufferGeometry().setFromPoints( [pointer,camera.position] );
// const line = new THREE.LineSegments( geometry, material );
// scene.add(line)
raycaster.setFromCamera( pointer, camera );
const intersects = raycaster.intersectObjects( earthPivot.children, false );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
console.log(intersects[0].object.name)
INTERSECTED = intersects[ 0 ].object;
}
} else {
INTERSECTED = null;
}
renderer.render( scene, camera );
} );
} );
// Convert a point [longitude, latitude] to a THREE.Vector3.
function vertex( point ) {
var lambda = point[ 0 ] * Math.PI / 180,
phi = point[ 1 ] * Math.PI / 180,
cosPhi = Math.cos(phi);
return new THREE.Vector3(
radius * cosPhi * Math.cos(lambda),
radius * cosPhi * Math.sin(lambda),
radius * Math.sin(phi)
);
}
function pointerMove( event ){
pointer.x = ( event.clientX / width ) * 2 - 1;
pointer.y = -( event.clientY / height ) * 2 + 1;
}
// Convert GeoJSON MultiLineString into spherical coordinates to THREE.LineSegments.
function wireframe( multilinestring, material ) {
var geometry = new THREE.BufferGeometry();
var pointsArray = new Array();
multilinestring.coordinates.forEach( function ( line ) {
d3.pairs( line.map( vertex ), function ( a, b ) {
pointsArray.push(a, b);
});
});
geometry.setFromPoints(pointsArray);
return new THREE.LineSegments(geometry, material);
}
</script>