3D Trajectory on a Map
Based on the sample log from Data Comets. For a better visualization, you can replace the URL of the tile provider to one that provides satellite images.
js
// SPDX-License-Identifier: Apache-2.0
// Copyright : J.P. Morgan Chase & Co.
import { Vector3, Scene, HemisphericLight, ArcRotateCamera } from '@babylonjs/core';
import * as anu from '@jpmorganchase/anu';
import * as d3 from 'd3';
import data from './data/drone-path.json';
import { XYZ } from 'ol/source';
import TileLayer from 'ol/layer/Tile';
export function trajectory3D(engine){
//Babylon boilerplate
const scene = new Scene(engine);
const light = new HemisphericLight('light1', new Vector3(0, 10, -10), scene)
const camera = new ArcRotateCamera("Camera", -(Math.PI / 4) * 3, Math.PI / 4, 10, new Vector3(0, 0, 0), scene);
camera.wheelPrecision = 20;
camera.minZ = 0;
camera.attachControl(true);
camera.position = new Vector3(2, 10, -15)
//Create a Texture Map
let textureMap = anu.createTextureMap('map',
{
layers: [new TileLayer({
source: new XYZ({ //Here we demonstrate overriding the default OpenStreetMap tile provider with a custom provider
crossOrigin: 'anonymous', //Required
urls: ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"] //Change these urls to your favorite tile provider that you have permission to use, here we still use OSM since it is free
}) //You can easily find these online by searching "XYZ tile providers"
})],
mapHeight: 1000,
mapWidth: 1000
});
let map = textureMap.map;
//Get the center lon/lat of our dataset
let center = [
(Math.min(...data.map(d => d.longitude)) + Math.max(...data.map(d => d.longitude))) / 2,
(Math.min(...data.map(d => d.latitude)) + Math.max(...data.map(d => d.latitude))) / 2
];
//Position and zoom map
map.getView().setCenter(center);
map.getView().setZoom(17);
//After the map has finished loading, render our trajectory
map.once('postrender', () => {
//Scales
let scaleLon = textureMap.scaleLon; //Use the provided scales from Texture Map
let scaleLat = textureMap.scaleLat; //You can replace these with other d3 scales for non-spatial data
let scaleAlt = d3.scaleLinear().domain([Math.min(...data.map(d => d.altitude)), Math.max(...data.map(d => d.altitude))]).range([0, 2]); //Altitude in our dataset is sea level, so the minimum altitude is the ground
let scaleC = d3.scaleSequential(anu.sequentialChromatic('OrRd').toColor3()).domain([0,Math.max(...data.map(d => d.velocity))])
//Vector3 array of the trajectory
let flightPath = data.map(d => new Vector3(scaleLon([d.longitude, d.latitude]), scaleAlt(d.altitude), scaleLat([d.longitude, d.latitude])));
//Color3 array of the trajectory for each segment
let flightColors = data.map(d => scaleC(d.velocity));
//Create chart and trajectory
let CoT = anu.create("cot", "chart");
let chart = anu.selectName("chart", scene);
let trajectories = chart.bind('greasedLine', { points: flightPath }) //Since we only have one trajectory, we can just pass in an array of Vector3 into the points parameter
.run((d,n,i) => { //Material properties of the greasedLine need to be set separately for now in .run()
n.greasedLineMaterial.useColors = true; //See for customization options: https://doc.babylonjs.com/typedoc/interfaces/BABYLON.IGreasedLineMaterial
n.greasedLineMaterial.colors = flightColors;
});
});
return scene;
}