|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>3D Earth with Country Labels</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.min.js"></script> |
|
<style> |
|
.country-label { |
|
position: absolute; |
|
color: white; |
|
background: rgba(0, 0, 0, 0.6); |
|
padding: 2px 6px; |
|
border-radius: 4px; |
|
font-size: 12px; |
|
pointer-events: none; |
|
transform: translate(-50%, -50%); |
|
white-space: nowrap; |
|
transition: all 0.2s ease; |
|
} |
|
|
|
.country-label:hover { |
|
background: rgba(0, 0, 0, 0.9); |
|
font-size: 14px; |
|
z-index: 100; |
|
} |
|
|
|
#earth-container { |
|
position: relative; |
|
width: 100%; |
|
height: 100vh; |
|
overflow: hidden; |
|
} |
|
|
|
.info-panel { |
|
position: absolute; |
|
bottom: 20px; |
|
left: 20px; |
|
background: rgba(0, 0, 0, 0.7); |
|
color: white; |
|
padding: 15px; |
|
border-radius: 8px; |
|
max-width: 300px; |
|
z-index: 10; |
|
} |
|
|
|
.controls-info { |
|
position: absolute; |
|
top: 20px; |
|
right: 20px; |
|
background: rgba(0, 0, 0, 0.7); |
|
color: white; |
|
padding: 10px; |
|
border-radius: 8px; |
|
font-size: 12px; |
|
z-index: 10; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-900"> |
|
<div id="earth-container"> |
|
<div class="controls-info"> |
|
<div>Left click + drag: Rotate</div> |
|
<div>Right click + drag: Pan</div> |
|
<div>Scroll: Zoom</div> |
|
</div> |
|
|
|
<div class="info-panel"> |
|
<h2 class="text-xl font-bold mb-2">Interactive 3D Earth</h2> |
|
<p class="mb-3">Explore our planet with labeled countries. Hover over labels for more information.</p> |
|
<div class="flex items-center mb-2"> |
|
<div class="w-4 h-4 bg-blue-500 rounded-full mr-2"></div> |
|
<span>Water</span> |
|
</div> |
|
<div class="flex items-center mb-2"> |
|
<div class="w-4 h-4 bg-green-600 rounded-full mr-2"></div> |
|
<span>Land</span> |
|
</div> |
|
<div class="flex items-center"> |
|
<div class="w-4 h-4 bg-yellow-400 rounded-full mr-2"></div> |
|
<span>Highlighted Countries</span> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
const container = document.getElementById('earth-container'); |
|
const width = container.clientWidth; |
|
const height = container.clientHeight; |
|
|
|
|
|
const scene = new THREE.Scene(); |
|
scene.background = new THREE.Color(0x000000); |
|
|
|
|
|
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); |
|
camera.position.z = 2; |
|
|
|
|
|
const renderer = new THREE.WebGLRenderer({ antialias: true }); |
|
renderer.setSize(width, height); |
|
container.appendChild(renderer.domElement); |
|
|
|
|
|
const controls = new THREE.OrbitControls(camera, renderer.domElement); |
|
controls.enableDamping = true; |
|
controls.dampingFactor = 0.05; |
|
|
|
|
|
const ambientLight = new THREE.AmbientLight(0x404040); |
|
scene.add(ambientLight); |
|
|
|
|
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); |
|
directionalLight.position.set(5, 3, 5); |
|
scene.add(directionalLight); |
|
|
|
|
|
const earthGeometry = new THREE.SphereGeometry(1, 64, 64); |
|
|
|
|
|
const textureLoader = new THREE.TextureLoader(); |
|
const earthTexture = textureLoader.load('https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg'); |
|
const bumpMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_normal_2048.jpg'); |
|
const specularMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_specular_2048.jpg'); |
|
|
|
const earthMaterial = new THREE.MeshPhongMaterial({ |
|
map: earthTexture, |
|
bumpMap: bumpMap, |
|
bumpScale: 0.05, |
|
specularMap: specularMap, |
|
specular: new THREE.Color('grey'), |
|
shininess: 5 |
|
}); |
|
|
|
const earth = new THREE.Mesh(earthGeometry, earthMaterial); |
|
scene.add(earth); |
|
|
|
|
|
const cloudGeometry = new THREE.SphereGeometry(1.01, 64, 64); |
|
const cloudTexture = textureLoader.load('https://threejs.org/examples/textures/planets/earth_clouds_1024.png'); |
|
const cloudMaterial = new THREE.MeshPhongMaterial({ |
|
map: cloudTexture, |
|
transparent: true, |
|
opacity: 0.4 |
|
}); |
|
|
|
const clouds = new THREE.Mesh(cloudGeometry, cloudMaterial); |
|
scene.add(clouds); |
|
|
|
|
|
const starGeometry = new THREE.BufferGeometry(); |
|
const starMaterial = new THREE.PointsMaterial({ |
|
color: 0xffffff, |
|
size: 0.05, |
|
transparent: true, |
|
opacity: 0.8 |
|
}); |
|
|
|
const starVertices = []; |
|
for (let i = 0; i < 5000; i++) { |
|
const x = (Math.random() - 0.5) * 2000; |
|
const y = (Math.random() - 0.5) * 2000; |
|
const z = (Math.random() - 0.5) * 2000; |
|
starVertices.push(x, y, z); |
|
} |
|
|
|
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3)); |
|
const stars = new THREE.Points(starGeometry, starMaterial); |
|
scene.add(stars); |
|
|
|
|
|
const countries = [ |
|
{ name: "United States", lat: 37.0902, lng: -95.7129, capital: "Washington, D.C.", population: "331 million" }, |
|
{ name: "Canada", lat: 56.1304, lng: -106.3468, capital: "Ottawa", population: "38 million" }, |
|
{ name: "Brazil", lat: -14.2350, lng: -51.9253, capital: "Brasília", population: "213 million" }, |
|
{ name: "United Kingdom", lat: 55.3781, lng: -3.4360, capital: "London", population: "67 million" }, |
|
{ name: "France", lat: 46.6035, lng: 1.8883, capital: "Paris", population: "67 million" }, |
|
{ name: "Germany", lat: 51.1657, lng: 10.4515, capital: "Berlin", population: "83 million" }, |
|
{ name: "China", lat: 35.8617, lng: 104.1954, capital: "Beijing", population: "1.4 billion" }, |
|
{ name: "India", lat: 20.5937, lng: 78.9629, capital: "New Delhi", population: "1.38 billion" }, |
|
{ name: "Japan", lat: 36.2048, lng: 138.2529, capital: "Tokyo", population: "126 million" }, |
|
{ name: "Australia", lat: -25.2744, lng: 133.7751, capital: "Canberra", population: "25 million" }, |
|
{ name: "Russia", lat: 61.5240, lng: 105.3188, capital: "Moscow", population: "146 million" }, |
|
{ name: "South Africa", lat: -30.5595, lng: 22.9375, capital: "Pretoria", population: "60 million" }, |
|
{ name: "Egypt", lat: 26.8206, lng: 30.8025, capital: "Cairo", population: "102 million" }, |
|
{ name: "Nigeria", lat: 9.0820, lng: 8.6753, capital: "Abuja", population: "206 million" }, |
|
{ name: "Mexico", lat: 23.6345, lng: -102.5528, capital: "Mexico City", population: "128 million" } |
|
]; |
|
|
|
|
|
const labels = []; |
|
|
|
countries.forEach(country => { |
|
const phi = (90 - country.lat) * (Math.PI / 180); |
|
const theta = (country.lng + 180) * (Math.PI / 180); |
|
|
|
const x = -Math.sin(phi) * Math.cos(theta); |
|
const y = Math.cos(phi); |
|
const z = Math.sin(phi) * Math.sin(theta); |
|
|
|
const label = document.createElement('div'); |
|
label.className = 'country-label'; |
|
label.textContent = country.name; |
|
label.dataset.capital = country.capital; |
|
label.dataset.population = country.population; |
|
|
|
container.appendChild(label); |
|
labels.push({ |
|
element: label, |
|
position: new THREE.Vector3(x, y, z) |
|
}); |
|
|
|
|
|
label.addEventListener('mouseover', () => { |
|
label.innerHTML = ` |
|
<div class="font-bold">${country.name}</div> |
|
<div>Capital: ${country.capital}</div> |
|
<div>Population: ${country.population}</div> |
|
`; |
|
}); |
|
|
|
label.addEventListener('mouseout', () => { |
|
label.textContent = country.name; |
|
}); |
|
}); |
|
|
|
|
|
window.addEventListener('resize', () => { |
|
camera.aspect = container.clientWidth / container.clientHeight; |
|
camera.updateProjectionMatrix(); |
|
renderer.setSize(container.clientWidth, container.clientHeight); |
|
}); |
|
|
|
|
|
function animate() { |
|
requestAnimationFrame(animate); |
|
|
|
|
|
earth.rotation.y += 0.001; |
|
clouds.rotation.y += 0.0015; |
|
|
|
controls.update(); |
|
|
|
|
|
labels.forEach(label => { |
|
const screenPosition = label.position.clone().project(camera); |
|
|
|
const x = (screenPosition.x * 0.5 + 0.5) * container.clientWidth; |
|
const y = (-(screenPosition.y * 0.5) + 0.5) * container.clientHeight; |
|
|
|
label.element.style.transform = `translate(-50%, -50%) translate(${x}px, ${y}px)`; |
|
|
|
|
|
label.element.style.display = screenPosition.z > 1 ? 'none' : 'block'; |
|
}); |
|
|
|
renderer.render(scene, camera); |
|
} |
|
|
|
animate(); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Linkmkb/earth3d" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |