three.js. Allows for full world-scale visualization of geographic data using tile-based chunks.MapProvider interface.



// Create a map tiles provider object
var provider = new OpenStreetMapsProvider();
// Create the map view and add it to your THREE scene
var map = new MapView(MapView.PLANAR, provider);
scene.add(map);
// By default, coordinates are in meters. Resize to kilometers:
map.scale.set(0.001, 0.001, 0.001);
UnitsUtils class to access the unit conversion methods. For example, to convert a latitude, longitude WGS84 pair value to XY coordinates you can use the code below:var coords = Geo.UnitsUtils.datumsToSpherical(40.940119, -8.535589);
controls.target.set(coords.x, 0, -coords.y);
Tiles are fetched from the service API configured. Each one of the services requires specific configuration using the specific MapProvider object.
Base tiles are always loaded at the beginning of the process. Each frame a couple of rays are cast into the tile tree. The number of rays can be configured using the MapView subdivisionRays attribute.
The distance of the ray to the camera is used to define if the node needs to simplified or sub-divided. These values can be configured using the thresholdUp and thresholdDown values.
The library has support for multiple data providers that must be configured beforehand. Most of these data providers rely on an external API that differs from service to service.
Each one of them has its own provider object implementation of the MapProvider interface.
The DebugProvider doesn't show geographic imager, but instead shows the zoom level and the coordinates on each tile.









LODControl can be implemented by extending the LODControl object and implementing the updateLOD(view, camera, renderer, scene) method.export class DistanceLOD extends LODControl
{
constructor() {super();}
updateLOD(view, camera, renderer, scene)
{
// Get world position of the camera.
var pov = new Vector3();
camera.getWorldPosition(pov);
view.traverse(function(node)
{
// Check if child in a MapNode
if(node instanceof MapNode)
{
var position = new Vector3();
node.getWorldPosition(position);
// Distance between camera and tile
var distance = pov.distanceTo(position);
// Normalize distance based on tile level
distance /= Math.pow(2, 20 - node.level);
// If closer than X subdivide
if (distance < 50)
{
node.subdivide();
}
// If far away, simplify parent
else if (distance > 200 node.parentNode)
{
node.parentNode.simplify();
}
}
});
}
}
MapNode objects are used to define how the tiles should be represented in the space. Every node implements the MapNode class inheriting from the THREE.Mesh class (every map node has a visual representation as a mesh).
MapNodes are organized hierarchically, where each node at zoom level N may have children at zoom level N+1.

MapProvider class and implement the fetchTile(zoom, x, y) method that returns a Promise with access to the tile data.export class OpenStreetMapsProvider extends MapProvider
{
constructor(address) {super();}
fetchTile(zoom, x, y)
{
return new Promise((resolve, reject) =>
{
var image = document.createElement("img");
image.onload = function(){resolve(image);};
image.onerror = function(){reject();};
image.crossOrigin = "Anonymous";
image.src = "https://a.tile.openstreetmap.org/" + zoom + "/" + x + "/" + y + ".png";
});
}
}
import {Color} from "three";
export class BlueToRedProvider extends MapProvider
{
fetchTile(zoom, x, y)
{
const canvas = new OffscreenCanvas(16, 16);
const context = canvas.getContext('2d');
const blue = new Color(0x0000FF);
const red = new Color(0xFF0000);
const color = blue.lerpHSL(red, (zoom - this.minZoom) / (this.maxZoom - this.minZoom));
context.fillStyle = color.getStyle();
context.fillRect(0, 0, 16, 16);
return Promise.resolve(canvas);
}
}
MapNode, we can create a new class that extends the base implementation.MapView object that is responsible for managing its life cycle.Mesh object and have attached a Geometry and Material used to render them into the scene.import {SphereGeometry, MeshBasicMaterial, Vector3} from "three";
// The MapNode inherits from three Mesh object and requires a geometry and material
export class CustomMapNode extends MapNode
{
constructor(parentNode = null, mapView = null, location = MapNode.ROOT, level = 0, x = 0, y = 0)
{
super(CustomMapNode.GEOMETRY, CustomMapNode.MATERIAL, parentNode, mapView, location, level, x, y);
}
static GEOMETRY = new SphereGeometry(0.5, 32, 32);
static MATERIAL = new MeshBasicMaterial();
// Base geometry applied to the map view.
static BASE_GEOMETRY = new MapNodeGeometry(1, 1, 1, 1);
// Base scale is applied to the map view
static BASE_SCALE = new Vector3(UnitsUtils.EARTH_PERIMETER, 1, UnitsUtils.EARTH_PERIMETER);
initialize() {
// Method to initialize data of the node (e.g fetch assets)
}
createChildNodes()
{
// Method called on subdivision to craete child nodes
}
}