i updated a code i got from here to add an addMarker function, but the locations on the map is off, any reason why?
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(
45,
innerWidth / innerHeight,
1,
2000
);
camera.position.set(0.5, 0.5, 1).setLength(14);
let renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0xaaffaa); //bg
document.body.appendChild(renderer.domElement);
let labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = "absolute";
labelRenderer.domElement.style.top = "0px";
document.body.appendChild(labelRenderer.domElement);
window.addEventListener("resize", onWindowResize);
let controls = new OrbitControls(camera, labelRenderer.domElement);
controls.enablePan = false;
controls.minDistance = 6;
controls.maxDistance = 15;
controls.enableDamping = true;
controls.autoRotate = true;
controls.autoRotateSpeed *= 0.25;
let globalUniforms = {
time: { value: 0 },
};
// <GLOBE>
let counter = 200000;
let rad = 5;
let sph = new THREE.Spherical();
let r = 0;
let dlong = Math.PI * (3 - Math.sqrt(5));
let dz = 2 / counter;
let long = 0;
let z = 1 - dz / 2;
let pts = [];
let clr = [];
let c = new THREE.Color();
let uvs = [];
for (let i = 0; i < counter; i++) {
r = Math.sqrt(1 - z * z);
let p = new THREE.Vector3(
Math.cos(long) * r,
z,
-Math.sin(long) * r
).multiplyScalar(rad);
pts.push(p);
z = z - dz;
long = long + dlong;
c.setHSL(0.45, 0.5, Math.random() * 0.25 + 0.25);
c.toArray(clr, i * 3);
sph.setFromVector3(p);
uvs.push(
(sph.theta + Math.PI) / (Math.PI * 2),
1.0 - sph.phi / Math.PI
);
}
let g = new THREE.BufferGeometry().setFromPoints(pts);
g.setAttribute("color", new THREE.Float32BufferAttribute(clr, 3));
g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
let m = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
onBeforeCompile: (shader) => {
shader.uniforms.globeTexture = {
value: new THREE.TextureLoader().load("texture.jpg"),
};
shader.vertexShader = `
uniform sampler2D globeTexture;
varying float vVisibility;
varying vec3 vNormal;
varying vec3 vMvPosition;
${shader.vertexShader}
`.replace(
`gl_PointSize = size;`,
`
vVisibility = texture(globeTexture, uv).g; // get value from texture
gl_PointSize = size * (vVisibility < 0.5 ? 1. : 0.75); // size depends on the value
vNormal = normalMatrix * normalize(position);
vMvPosition = -mvPosition.xyz;
gl_PointSize *= 0.4 + (dot(normalize(vMvPosition), vNormal) * 0.6); // size depends position in camera space
`
);
//console.log(shader.vertexShader);
shader.fragmentShader = `
varying float vVisibility;
varying vec3 vNormal;
varying vec3 vMvPosition;
${shader.fragmentShader}
`.replace(
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
bool circ = length(gl_PointCoord - 0.5) > 0.5; // make points round
bool vis = dot(vMvPosition, vNormal) < 0.; // visible only on the front side of the sphere
if (circ || vis) discard;
vec3 col = diffuse + (vVisibility > 0.5 ? 0.5 : 0.); // make oceans brighter
vec4 diffuseColor = vec4( col, opacity );
`
);
//console.log(shader.fragmentShader);
},
});
let globe = new THREE.Points(g, m);
scene.add(globe);
// <Markers>
const markerCount = 30;
let markerInfo = []; // information on markers
let gMarker = new THREE.PlaneGeometry();
let mMarker = new THREE.MeshBasicMaterial({
color: 0xff3232,
onBeforeCompile: (shader) => {
shader.uniforms.time = globalUniforms.time;
shader.vertexShader = `
attribute float phase;
varying float vPhase;
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
vPhase = phase; // de-synch of ripples
`
);
//console.log(shader.vertexShader);
shader.fragmentShader = `
uniform float time;
varying float vPhase;
${shader.fragmentShader}
`.replace(
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
vec2 lUv = (vUv - 0.5) * 2.;
float val = 0.;
float lenUv = length(lUv);
val = max(val, 1. - step(0.25, lenUv)); // central circle
val = max(val, step(0.4, lenUv) - step(0.5, lenUv)); // outer circle
float tShift = fract(time * 0.5 + vPhase);
val = max(val, step(0.4 + (tShift * 0.6), lenUv) - step(0.5 + (tShift * 0.5), lenUv)); // ripple
if (val < 0.5) discard;
vec4 diffuseColor = vec4( diffuse, opacity );`
);
//console.log(shader.fragmentShader)
},
});
mMarker.defines = { USE_UV: " " }; // needed to be set to be able to work with UVs
let markers = new THREE.InstancedMesh(gMarker, mMarker, markerCount);
let dummy = new THREE.Object3D();
let phase = [];
// Function to convert latitude and longitude to 3D coordinates
function latLngToVector3(lat, lng, radius) {
const phi = (90 - lat) * (Math.PI / 180);
const theta = (lng + 180) * (Math.PI / 180);
const x = -(radius * Math.sin(phi) * Math.cos(theta));
const y = radius * Math.cos(phi);
const z = radius * Math.sin(phi) * Math.sin(theta);
return new THREE.Vector3(x, y, z);
}
// Function to add a marker
function addMarker(lat, lng, magnitude, countryName, flagURL) {
const dummy = new THREE.Object3D(); // Ensure dummy is defined locally
// Calculate position similar to the for loop
dummy.position.copy(latLngToVector3(lat, lng, rad + 0.1));
dummy.position.setLength(rad + 0.1); // Ensure position length matches the for loop
// Orient marker
dummy.lookAt(dummy.position.clone().setLength(rad + 1)); // Match lookAt logic in the for loop
dummy.updateMatrix();
// Add marker to instance
const markerIndex = markerInfo.length;
markers.setMatrixAt(markerIndex, dummy.matrix);
markers.instanceMatrix.needsUpdate = true;
// Store marker information
markerInfo.push({
id: markerIndex + 1,
mag: magnitude || THREE.MathUtils.randInt(1, 10), // Default to random if not provided
crd: dummy.position.clone(),
country: countryName,
flag: flagURL,
});
}
// Example usage: Add markers for specific countries
const locations = [
{
name: "Nigeria",
flag: "https://www.worldometers.info/img/flags/small/tn_ni-flag.gif",
lat: 9.081999,
lng: 8.675277,
},
{
name: "Mexico",
flag: "https://www.worldometers.info/img/flags/small/tn_mx-flag.gif",
lat: 23.634501,
lng: -102.552788,
},
{
name: "Brazil",
flag: "https://www.worldometers.info/img/flags/small/tn_br-flag.gif",
lat: -14.235,
lng: -51.9253,
},
{
name: "Colombia",
flag: "https://www.worldometers.info/img/flags/small/tn_co-flag.gif",
lat: 4.5709,
lng: -74.2973,
},
{
name: "Paraguay",
flag: "https://www.worldometers.info/img/flags/small/tn_pa-flag.gif",
lat: -23.4425,
lng: -58.4438,
},
{
name: "Costa Rica",
flag: "https://www.worldometers.info/img/flags/small/tn_cs-flag.gif",
lat: 9.7489,
lng: -83.7534,
},
{
name: "Spain",
flag: "https://www.worldometers.info/img/flags/small/tn_sp-flag.gif",
lat: 40.4637,
lng: -3.7492,
},
{
name: "France",
flag: "https://www.worldometers.info/img/flags/small/tn_fr-flag.gif",
lat: 46.6034,
lng: 1.8883,
},
{
name: "Germany",
flag: "https://www.worldometers.info/img/flags/small/tn_gm-flag.gif",
lat: 51.1657,
lng: 10.4515,
},
{
name: "Lithuania",
flag: "https://www.worldometers.info/img/flags/small/tn_lh-flag.gif",
lat: 55.1694,
lng: 23.8813,
},
{
name: "Indonesia",
flag: "https://www.worldometers.info/img/flags/small/tn_id-flag.gif",
lat: -0.7893,
lng: 113.9213,
},
];
locations.forEach((place) => {
addMarker(place.lat, place.lng, 0, place.name, place.flag);
});
// addMarker(37.7749, -122.4194, 5); // San Francisco
// addMarker(51.5074, -0.1278, 7); // London
// addMarker(35.6895, 139.6917, 8); // Tokyo
gMarker.setAttribute(
"phase",
new THREE.InstancedBufferAttribute(new Float32Array(phase), 1)
);
scene.add(markers);
// </Markers>
// <Label>
let labelDiv = document.getElementById("markerLabel");
let closeBtn = document.getElementById("closeButton");
closeBtn.addEventListener("pointerdown", (event) => {
labelDiv.classList.add("hidden");
});
let label = new CSS2DObject(labelDiv);
label.userData = {
cNormal: new THREE.Vector3(),
cPosition: new THREE.Vector3(),
mat4: new THREE.Matrix4(),
trackVisibility: () => {
// the closer to the edge, the less opacity
let ud = label.userData;
ud.cNormal
.copy(label.position)
.normalize()
.applyMatrix3(globe.normalMatrix);
ud.cPosition
.copy(label.position)
.applyMatrix4(
ud.mat4.multiplyMatrices(
camera.matrixWorldInverse,
globe.matrixWorld
)
);
let d = ud.cPosition.negate().normalize().dot(ud.cNormal);
d = smoothstep(0.2, 0.7, d);
label.element.style.opacity = d;
// https://github.com/gre/smoothstep/blob/aa5b60a10fd7c1c6e3a62d5efad4b086369f4232/index.js
function smoothstep(min, max, value) {
var x = Math.max(0, Math.min(1, (value - min) / (max - min)));
return x * x * (3 - 2 * x);
}
},
};
scene.add(label);
// </Label>
// <Interaction>
let pointer = new THREE.Vector2();
let raycaster = new THREE.Raycaster();
let intersections;
let divID = document.getElementById("idNum");
let divMag = document.getElementById("magnitude");
let divCrd = document.getElementById("coordinates");
let divCountry = document.getElementById("country");
let divFlag = document.getElementById("flag");
window.addEventListener("pointerdown", (event) => {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
intersections = raycaster.intersectObject(markers).filter((m) => {
return m.uv.subScalar(0.5).length() * 2 < 0.25; // check, if we're in the central circle only
});
//console.log(intersections);
if (intersections.length > 0) {
let iid = intersections[0].instanceId;
let mi = markerInfo[iid];
// divID.innerHTML = `ID: <b>${mi.id}</b>`;
// divMag.innerHTML = `Mag: <b>${mi.mag}</b>`;
// divCrd.innerHTML = `X: <b>${mi.crd.x.toFixed(
// 2
// )}</b>; Y: <b>${mi.crd.y.toFixed(2)}</b>; Z: <b>${mi.crd.z.toFixed(
// 2
// )}</b>`;
// Display Country Name and Flag
divCountry.innerHTML = `<b>${mi.country}</b>`;
divFlag.innerHTML = `<img src="${mi.flag}" alt="${mi.country} flag" style="width: 50px; height: 50px;object-fit:contain;">`;
label.position.copy(mi.crd);
label.element.animate(
[
{
width: "0px",
height: "0px",
marginTop: "0px",
marginLeft: "0px",
},
{
width: "230px",
height: "50px",
marginTop: "-25px",
maginLeft: "120px",
},
],
{
duration: 250,
}
);
label.element.classList.remove("hidden");
}
});
// </Interaction>
let clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
let t = clock.getElapsedTime();
globalUniforms.time.value = t;
label.userData.trackVisibility();
controls.update();
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
});
function onWindowResize() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
labelRenderer.setSize(innerWidth, innerHeight);
}