Documentation Index Fetch the complete documentation index at: https://mintlify.com/jscastro76/threebox/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Threebox handles three different coordinate systems:
Geographic coordinates (longitude/latitude)
Meters (real-world metric units)
World units (Three.js scene units)
Understanding how to convert between these systems is essential for positioning and scaling 3D objects correctly.
Coordinate System Types
1. Geographic Coordinates (lnglat)
Format : [longitude, latitude, altitude]
Longitude: -180 to 180 (west to east)
Latitude: -90 to 90 (south to north)
Altitude: meters above sea level (optional)
const coords = [ - 122.4194 , 37.7749 , 0 ]; // San Francisco
2. Meters
Real-world metric measurements for object dimensions:
const building = {
width: 50 , // meters
length: 30 , // meters
height: 100 // meters
};
3. World Units
Three.js scene coordinates using Mercator projection:
Based on Spherical Mercator projection
World size: 536870912 units (2^29)
Centered at [0, 0]
const worldPos = new THREE . Vector3 ( x , y , z );
Key Constants
From source (constants.js):
const WORLD_SIZE = 1024000 ;
const MERCATOR_A = 6378137.0 ; // WGS84 ellipsoid semi-major axis
const DEG2RAD = Math . PI / 180 ;
const PROJECTION_WORLD_SIZE = WORLD_SIZE / ( MERCATOR_A * DEG2RAD * 2 );
const EARTH_CIRCUMFERENCE = 40075017 ; // meters at equator
Converting Coordinates
lnglat → World Units
Use projectToWorld(coords) to convert geographic coordinates to Three.js world coordinates.
From source (utils.js:98-118):
projectToWorld : function ( coords ) {
// Spherical Mercator forward projection
var projected = [
- MERCATOR_A * DEG2RAD * coords [ 0 ] * PROJECTION_WORLD_SIZE ,
- MERCATOR_A * Math . log (
Math . tan (( Math . PI * 0.25 ) + ( 0.5 * DEG2RAD * coords [ 1 ]))
) * PROJECTION_WORLD_SIZE
];
// Z dimension (altitude)
if ( ! coords [ 2 ]) {
projected . push ( 0 )
} else {
var pixelsPerMeter = this . projectedUnitsPerMeter ( coords [ 1 ]);
projected . push ( coords [ 2 ] * pixelsPerMeter );
}
var result = new THREE . Vector3 ( projected [ 0 ], projected [ 1 ], projected [ 2 ]);
return result ;
}
Example :
const lnglat = [ - 122.4194 , 37.7749 , 10 ];
const worldPos = tb . projectToWorld ( lnglat );
// Returns THREE.Vector3(x, y, z)
World Units → lnglat
Use unprojectFromWorld(worldUnits) to convert Three.js world coordinates back to geographic coordinates.
From source (utils.js:151-166):
unprojectFromWorld : function ( worldUnits ) {
var unprojected = [
- worldUnits . x / ( MERCATOR_A * DEG2RAD * PROJECTION_WORLD_SIZE ),
2 * ( Math . atan ( Math . exp (
worldUnits . y / ( PROJECTION_WORLD_SIZE * ( - MERCATOR_A ))
)) - Math . PI / 4 ) / DEG2RAD
];
var pixelsPerMeter = this . projectedUnitsPerMeter ( unprojected [ 1 ]);
// Z dimension (altitude in meters)
var height = worldUnits . z || 0 ;
unprojected . push ( height / pixelsPerMeter );
return unprojected ;
}
Example :
const worldPos = new THREE . Vector3 ( 1000 , 2000 , 100 );
const lnglat = tb . unprojectFromWorld ( worldPos );
// Returns [lng, lat, altitude]
Units Per Meter Scaling
Projected Units Per Meter
Convert meters to world units at a specific latitude:
projectedUnitsPerMeter : function ( latitude ) {
return Math . abs (
WORLD_SIZE /
Math . cos ( DEG2RAD * latitude ) /
EARTH_CIRCUMFERENCE
);
}
Why latitude matters : The Mercator projection stretches as you move away from the equator. A meter at the poles represents more projection units than at the equator.
Example :
// At equator (lat = 0)
const unitsPerMeterEquator = tb . projectedUnitsPerMeter ( 0 );
// ≈ 25.56
// At San Francisco (lat = 37.7749)
const unitsPerMeterSF = tb . projectedUnitsPerMeter ( 37.7749 );
// ≈ 32.39 (larger because of Mercator distortion)
// Convert 100 meters to world units in SF
const worldUnits = 100 * unitsPerMeterSF ;
Scaling Vertices to Meters
From source (utils.js:132-141):
_scaleVerticesToMeters : function ( centerLatLng , vertices ) {
var pixelsPerMeter = this . projectedUnitsPerMeter ( centerLatLng [ 1 ]);
var centerProjected = this . projectToWorld ( centerLatLng );
for ( var i = 0 ; i < vertices . length ; i ++ ) {
vertices [ i ]. multiplyScalar ( pixelsPerMeter );
}
return vertices ;
}
This scales Three.js geometry vertices from meters to world units at a specific location.
Object Positioning
Using ‘meters’ Units
When creating objects with units: 'meters', Threebox automatically handles scaling:
tb . loadObj ({
obj: '/models/building.glb' ,
type: 'gltf' ,
scale: 1 ,
units: 'meters' , // Object dimensions in real-world meters
rotation: { x: 90 , y: 0 , z: 0 }
}, function ( model ) {
model . setCoords ([ - 122.4194 , 37.7749 , 0 ]);
tb . add ( model );
});
Threebox will:
Convert lnglat to world units
Scale the object based on latitude
Update scale when zooming to maintain real-world size
Using ‘scene’ Units
With units: 'scene', objects use raw Three.js units without latitude scaling:
const obj = tb . Object3D ({
obj: mesh ,
units: 'scene' // No automatic scaling
});
Useful for:
Non-geographic objects
Custom coordinate systems
Objects that shouldn’t scale with latitude
Altitude Handling
Altitude in Meters
Altitudes are always specified in meters above sea level:
// Place object 50 meters above ground
model . setCoords ([ - 122.4194 , 37.7749 , 50 ]);
Mercator Z from Altitude
From source (utils.js:128-130):
mercatorZfromAltitude : function ( altitude , lat ) {
return altitude / this . _circumferenceAtLatitude ( lat );
}
Where:
_circumferenceAtLatitude : function ( latitude ) {
return EARTH_CIRCUMFERENCE * Math . cos ( latitude * Math . PI / 180 );
}
This converts altitude in meters to Mercator Z coordinate.
Positioning Objects
setCoords Method
All Threebox objects have a setCoords method:
obj . setCoords ([ lng , lat , altitude ]);
This:
Converts lnglat to world units
Positions the object in the Three.js scene
Stores coordinates in obj.coordinates
Getting Object Position
// Get geographic coordinates
const coords = obj . coordinates ;
// [lng, lat, altitude]
// Get Three.js world position
const worldPos = obj . position ;
// THREE.Vector3(x, y, z)
Feature Center Calculation
For GeoJSON features, calculate the center point:
From source (utils.js:188-214):
getFeatureCenter : function ( feature , model , level ) {
let center = [];
let latitude = 0 ;
let longitude = 0 ;
let height = 0 ;
let coordinates = [ ... feature . geometry . coordinates [ 0 ]];
if ( feature . geometry . type === "Point" ) {
center = [ ... coordinates [ 0 ]];
} else {
// For Polygon/MultiPolygon
if ( feature . geometry . type === "MultiPolygon" )
coordinates = coordinates [ 0 ];
// Remove duplicate first/last coordinate
coordinates . splice ( - 1 , 1 );
coordinates . forEach ( function ( c ) {
latitude += c [ 0 ];
longitude += c [ 1 ];
});
center = [
latitude / coordinates . length ,
longitude / coordinates . length
];
}
height = this . getObjectHeightOnFloor ( feature , model , level );
( center . length < 3 ? center . push ( height ) : center [ 2 ] = height );
return center ;
}
Example :
const feature = {
type: 'Feature' ,
geometry: {
type: 'Polygon' ,
coordinates: [[ ... ]]
},
properties: {
height: 50 ,
base_height: 0
}
};
const center = tb . getFeatureCenter ( feature );
// Returns [lng, lat, altitude]
Object Height on Floor
Calculate altitude for objects on building levels:
From source (utils.js:216-225):
getObjectHeightOnFloor : function ( feature , obj , level = feature . properties . level || 0 ) {
let floorHeightMin = ( level * ( feature . properties . levelHeight || 0 ));
let base = ( feature . properties . base_height || feature . properties . min_height || 0 );
let height = (( obj && obj . model ) ? 0 : ( feature . properties . height - base ));
let objectHeight = height + base ;
let modelHeightFloor = floorHeightMin + objectHeight ;
return modelHeightFloor ;
}
Example :
const altitude = tb . getObjectHeightOnFloor ( feature , model , 2 );
// Returns altitude in meters for level 2
Coordinate System Conversions Summary
From To Method lnglat World units tb.projectToWorld(coords)World units lnglat tb.unprojectFromWorld(vector3)Meters World units meters * tb.projectedUnitsPerMeter(lat)World units Meters worldUnits / tb.projectedUnitsPerMeter(lat)Altitude Mercator Z tb.utils.mercatorZfromAltitude(altitude, lat)
Practical Examples
Example 1: Place Object at Address
// Geocode address to lnglat (using external service)
const address = "Golden Gate Bridge, San Francisco" ;
const lnglat = [ - 122.4783 , 37.8199 ];
const altitude = 67 ; // meters above sea level
// Load and position model
tb . loadObj ({
obj: '/models/landmark.glb' ,
type: 'gltf' ,
scale: 1 ,
units: 'meters'
}, function ( model ) {
model . setCoords ([ ... lnglat , altitude ]);
tb . add ( model );
});
Example 2: Create Grid of Objects
const centerLat = 37.7749 ;
const centerLng = - 122.4194 ;
const spacing = 100 ; // meters
for ( let x = - 2 ; x <= 2 ; x ++ ) {
for ( let y = - 2 ; y <= 2 ; y ++ ) {
// Calculate offset in degrees (approximate)
const latOffset = ( y * spacing ) / 111320 ; // degrees per meter lat
const lngOffset = ( x * spacing ) / ( 111320 * Math . cos ( centerLat * Math . PI / 180 ));
const position = [
centerLng + lngOffset ,
centerLat + latOffset ,
0
];
// Create object at position
const cube = createCube ();
cube . setCoords ( position );
tb . add ( cube );
}
}
Example 3: Convert Screen to World Coordinates
map . on ( 'click' , function ( e ) {
// e.point = screen coordinates {x, y}
// e.lngLat = geographic coordinates
// Convert to world units
const worldPos = tb . projectToWorld ([
e . lngLat . lng ,
e . lngLat . lat ,
0
]);
console . log ( 'Screen:' , e . point );
console . log ( 'Geographic:' , e . lngLat );
console . log ( 'World:' , worldPos );
});
Example 4: Measure Distance Between Objects
const obj1 = model1 . coordinates ; // [lng, lat, alt]
const obj2 = model2 . coordinates ;
// Convert to world units
const world1 = tb . projectToWorld ( obj1 );
const world2 = tb . projectToWorld ( obj2 );
// Calculate distance in world units
const worldDistance = world1 . distanceTo ( world2 );
// Convert back to meters
const avgLat = ( obj1 [ 1 ] + obj2 [ 1 ]) / 2 ;
const pixelsPerMeter = tb . projectedUnitsPerMeter ( avgLat );
const metersDistance = worldDistance / pixelsPerMeter ;
console . log ( `Distance: ${ metersDistance . toFixed ( 2 ) } meters` );
Common Issues
Objects appear too large/small
Check units option is set correctly (‘meters’ vs ‘scene’)
Verify scale parameter
Ensure coordinates are [lng, lat], not [lat, lng]
Objects distorted at high latitudes
Mercator projection stretches near poles
Use units: 'meters' for automatic compensation
Consider alternative projection for polar regions
Altitude doesn’t match expected height
Altitude is meters above sea level, not ground level
Account for base_height in features
Terrain elevation affects perceived altitude
Best Practices
Always use meters for real-world objects
units : 'meters' // Recommended for geographic accuracy
Store original coordinates
obj . userData . originalCoords = [ lng , lat , alt ];
Use helper methods
// Don't manually calculate
const world = tb . projectToWorld ( lnglat ); // Use helper
Account for latitude scaling
const scale = tb . projectedUnitsPerMeter ( latitude );
Next Steps
Threebox Instance Learn about Threebox initialization and configuration
Custom Layers Integrate Threebox with Mapbox custom layers
Camera Synchronization Understand camera synchronization between Three.js and Mapbox