问题描述
我有一个使用three.js构建的地球仪
Refrene:
再次仅供参考,请参阅makc的这个现成的类:
I have a globe built using three.js
Refrene:http://callumprentice.github.io/apps/globe_manipulator/index.html
I need to show a HTML div at a specific lattitude/longitude, can you tell me how to position the div at some specific lat/long?
What have i tried:I am not even able to proceed with the conversion of lat lon to screen x and y cordinates.
My JS code:
var camera, scene, renderer, controls, root_object = 0, mesh;
var uniform_color = true, uniform_height = true, extrusion_amount = 5.0;
var helper_1, helper_2;
var radius;
init();
animate();
function _convertLatLonToVec3 (lat,lon) {
lat = lat * Math.PI / 180.0;
lon = -lon * Math.PI / 180.0;
return new THREE.Vector3(
Math.cos(lat) * Math.cos(lon), //rechts links invert
Math.sin(lat), // up down invert
Math.cos(lat) * Math.sin(lon));
}
function InfoBox( city, mesh, radius, domElement )
{
var position = _convertLatLonToVec3( city.lat, city.lng ).multiplyScalar( radius );
this.mesh = mesh;
this.mesh.position.copy( position );
this._screenVector = new THREE.Vector3(0,0,0);
this.box = document.createElement( 'div' );
this.box.innerHTML = city.name;
this.box.className = "hudLabel";
this.domElement = domElement;
this.domElement.appendChild( this.box );
}
InfoBox.prototype.update = function ()
{
this._screenVector.set( 0, 0, 0 );
this.mesh.localToWorld( this._screenVector );
this._screenVector.project( camera );
var posx = Math.round(( this._screenVector.x + 1 ) * this.domElement.offsetWidth / 2 );
var posy = Math.round(( 1 - this._screenVector.y ) * this.domElement.offsetHeight / 2 );
var boundingRect = this.box.getBoundingClientRect();
this.box.style.left = ( posx - boundingRect.width ) + 'px';
this.box.style.top = posy + 'px';
};
function init() {
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0x131C1D, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 400;
camera.position.y = 400;
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(1, 0, 0);
scene.add(light);
var light2 = new THREE.DirectionalLight(0xffffff);
light2.position.set(0, 0, 1);
scene.add(light2);
radius = 300;
var segments = 128;
var rings = 128;
var geometry = new THREE.SphereGeometry(radius, segments, rings);
material_map = THREE.ImageUtils.loadTexture('textures/sea_texture.jpg');
material_map.wrapS = THREE.RepeatWrapping;
material_map.wrapT = THREE.RepeatWrapping;
material_map.repeat.set(8, 8);
material = new THREE.MeshPhongMaterial({
//map: material_map,
color: 0xFFffffff,
opacity: 0,
transparent: true
});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var helper_geometry = new THREE.SphereGeometry( 2, 32, 32 );
helper_1 = new THREE.Mesh( helper_geometry, new THREE.MeshNormalMaterial() );
scene.add( helper_1 )
helper_2 = new THREE.Mesh( helper_geometry, new THREE.MeshNormalMaterial() );
scene.add( helper_2 )
add_all_countries();
controls = new THREE.TrackballControls(camera, renderer.domElement);
globe_manipulator = new globe_manipulator({
dom_object: renderer.domElement,
camera: camera,
radius: radius,
on_clicked_callback: onClicked,
auto_rotate: true,
right_click_to_select: true,
start_lat: 0.0,
start_lng: 0.0,
start_distance: 900.0,
min_distance: 560.0,
max_distance: 1200.0,
mesh: mesh
});
gotoRandomCity();
window.addEventListener('resize', onWindowResize, false);
}
function onClicked(event) {
if ( event.mouse_event.button == 2 ) {
if ( event.intersects ) {
actual_latlng = "Lat: " + event.lat.toFixed(2) + " Lng: " + event.lng.toFixed(2);
var phi = (90.0 - event.lat) * Math.PI / 180.0;
var theta = (360.0 - event.lng) * Math.PI / 180.0;
var x = radius * Math.sin(phi) * Math.cos(theta);
var y = radius * Math.cos(phi);
var z = radius * Math.sin(phi) * Math.sin(theta);
helper_2.position.set( x, y, z );
}
else {
actual_latlng = "Missed";
}
}
}
function gotoRandomCity() {
var name = "Delhi";
var lat = 28.6100;
var lng = 77.2300;
var phi = (90.0 - lat) * Math.PI / 180.0;
var theta = (360.0 - lng) * Math.PI / 180.0;
var x = radius * Math.sin(phi) * Math.cos(theta);
var y = radius * Math.cos(phi);
var z = radius * Math.sin(phi) * Math.sin(theta);
helper_1.position.set( x, y, z );
city_name = name;
target_latlng = "Lat: " + lat.toFixed(2) + " Lng: " + lng.toFixed(2);
console.log(lat+" "+lng);
globe_manipulator.set_lat_lng(lat, lng);
var marker = new THREE.Mesh( new THREE.SphereGeometry( 5, 16, 16 ) );
scene.add( marker );
var city = { "name": "Hello", "lat": lat, "lng": lng };
label = new InfoBox( city, marker, radius, document.body );
}
function add_country(shape_points) {
var shape = new THREE.Shape(shape_points);
var shape_geom;
var inner_radius = 300.0;
var outer_radius = 305.0;
shape_geom = shape.extrude({
amount: outer_radius - inner_radius,
bevelEnabled: false
});
var offset = 0;
if ( ! uniform_height )
offset = Math.random() * extrusion_amount;
shape_geom.vertices.forEach(function (vert, index) {
var radius = 0.0;
if (index < shape_geom.vertices.length / 2) {
radius = inner_radius;
} else {
radius = inner_radius + extrusion_amount + offset;
}
var phi = (90.0 - vert.y) * Math.PI / 180.0;
var theta = (360.0 - vert.x) * Math.PI / 180.0;
vert.x = radius * Math.sin(phi) * Math.cos(theta);;
vert.y = radius * Math.cos(phi);;
vert.z = radius * Math.sin(phi) * Math.sin(theta);;
});
var color = new THREE.Color(0x90BAC0);
if (! uniform_color)
color.setHSL(Math.random(),0.8,0.8 );
var shape_material = new THREE.MeshPhongMaterial({
color: color,
side: THREE.DoubleSide
});
var shape_mesh = new THREE.Mesh(shape_geom, shape_material);
root_object.add(shape_mesh);
}
function add_all_countries() {
if ( root_object ) {
scene.remove(root_object);
}
root_object = new THREE.Object3D();
scene.add(root_object);
countries.features.forEach(function (country) {
if (country.geometry.coordinates.length === 1) {
var shape_points = [];
country.geometry.coordinates[0].forEach(function (points) {
shape_points.push(new THREE.Vector2(points[0], points[1]));
});
add_country(shape_points);
} else {
country.geometry.coordinates.forEach(function (coord_set) {
if (coord_set.length == 1) {
var shape_points = [];
coord_set[0].forEach(function (points) {
shape_points.push(new THREE.Vector2(points[0], points[1]));
});
add_country(shape_points);
} else {
var shape_points = [];
coord_set.forEach(function (points) {
shape_points.push(new THREE.Vector2(points[0], points[1]));
});
add_country(shape_points);
}
});
}
});
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
globe_manipulator.update();
//controls.update();
///console.log(label_html);
label.update();
renderer.render(scene, camera);
/*
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);*/
}
And globe_manipulator is not creating the issue.
Im posting this as an answer now because its exceeding the comments intention.
First, convert your Lat/Long to a THREE.Vector3
. You could use this function:
function convertLatLonToVec3(lat,lon) {
lat = lat * Math.PI / 180.0;
lon = -lon * Math.PI / 180.0;
return new THREE.Vector3(
Math.cos(lat) * Math.cos(lon),
Math.sin(lat),
Math.cos(lat) * Math.sin(lon));
};
You multiply this Vector3 by the radius of your sphere (globe) to get the position:
var radius = 100;
var position = convertLatLonToVec3( city.lat, city.lng ).multiplyScalar( radius );
This is your position in 3D-Space.
Next step get the screen space position from the 3D-Space position and update the HTML Overlay box-position. In short this is how it looks:
var screenVector = new THREE.Vector3( 0, 0, 0 );
screenVector.copy( position );
screenVector.project( camera );
var posx = Math.round(( screenVector.x + 1 ) * domElement.offsetWidth / 2 );
var posy = Math.round(( 1 - screenVector.y ) * domElement.offsetHeight / 2 );
// the html overlaybox
this.box.style.left = posx + 'px';
this.box.style.top = posy + 'px';
This needs to be done on every update, makc is hijacking the Object3D for this.
This is a simple fiddle which may helps you understandinghttp://jsfiddle.net/L0rdzbej/144/
Again only for reference, see this ready-made class from makc: three-js-and-2d-overlays
这篇关于three.js globe中的HTML元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!