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 to meter, can be resized to kilometer
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 bellow: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, then each frame a couple of rays are casted 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 external API that differ from service to service.
Each one of them has its own provider object implementation of the MapProvider
interface.
The DebugProvider
provides information about the tiles loaded, shows the zoom level and the coordinates of the tile relative to the origin in that specific level.
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. MapNode
are organized hierarchically, every node implements the MapNode class that is based on the THREE.Mesh class (every map node has a visual representation as a mesh).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
}
}
Generated using TypeDoc