Skip to content

Mesh Map

Overview

The Mesh Map prefab takes a GeoJson and d3-geo projection input and generates a Extruded Polygon Mesh for each polygon or multi-polygon GeoJSON feature. This prefab also utilize topoJson for Mesh simplification. The createMeshMap() method returns an instance of the MeshMap object which contains the relevant properties for the Mesh creation and manipulation. Each polygon metadata.data property will be set with the data from its features.properties GeoJSON.

Usage

js
//name (required), options (geojson required), babylon scene (optional) 
//Returns instance of MeshMap
let map = anu.createMeshMap(name: String, options: {}, scene: BABYLON.Scene);

//The transformed input d3-geo projection
let projection = map.projection

//A selection object containing all the generated polygons
let selection = map.selection

Options

OptionValueDefault
geoJson (required)a valid geoJSON filenull
projection(d3.GeoProjection) such as d3.geoAlbers()d3.geoAlbers()
size([number, number]) an array with the max height and width of the projection[10,10]
transform([number, number]) and array with the center coordinates of the projection in render space[0,0]
simplification(number) the simplification factor relative to the projection size0
depth(number) the height of the extruded the polygons1
cot(Babylon.Node) The node to be the parent of all the polygons, if not set a transform node "meshMapCOT" will be createdTransformNode('meshMapCOT')

Methods and Properties

Property / MethodDescription
projectionthe transformed d3.geoProjection which can be used map position from longitude and latitude as projection([lon, lat])
selectiona selection object containing all the generated polygons with their respective geoJSON properties data binded to them

Examples

js
// SPDX-License-Identifier: Apache-2.0
// Copyright : J.P. Morgan Chase & Co.

import * as anu from '@jpmorganchase/anu';
import * as BABYLON from '@babylonjs/core';
import * as d3 from 'd3';
import data from './data/airports.csv';
import geoJ from './data/gz_2010_us_040_00_5m.json';

//Create and export a function that takes a Babylon engine and returns a Babylon Scene
export function meshMap(engine) {

  //Create an empty Scene
  const scene = new BABYLON.Scene(engine);
  //Add some lighting
  new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 10, 0), scene);
  //Add a camera that rotates around the origin and adjust its properties
  const camera = new BABYLON.ArcRotateCamera('Camera', 0, 0, 0, new BABYLON.Vector3(0, 0, 0), scene);
  camera.position = new BABYLON.Vector3(0, 1.25, -1);
  camera.wheelPrecision = 20;
  camera.minZ = 0;
  camera.attachControl(true);

  //Use the Mesh Map prefab to create 3D meshes based on GeoJSON data
  let meshMap = anu.createMeshMap('meshMap', { geoJson: geoJ,
                                               depth: 0.05,
                                               projection: d3.geoAlbers().reflectY(true), //Flip the Y so we don't have to rotate it later
                                               size: [2, 2],
                                               simplification: 0.000001 });  //Higher value = less vertices = better performance but less detail

  //The prefab contains a Selection that consists of the Meshes for each polygon in our GeoJSON (i.e., the US states)
  let states = meshMap.selection;

  //Create a D3 scale for color, using Anu helper functions map scale outputs to StandardMaterial objects based on the 'schemecategory10' palette from D3
  let scaleC = d3.scaleOrdinal(anu.ordinalChromatic('d310').toStandardMaterial());

  //Assign each state a Material (and therefore, their color)
  states.material((d) => scaleC(d.NAME));

  
  //We use Mesh instancing here for better performance, first we create a Mesh that serves as the root Node
  let rootSphere = anu.create('sphere', 'sphere', { diameter: 0.003 });
  rootSphere.isVisible = false;
  rootSphere.registerInstancedBuffer('color', 4);   //We need an InstancedBuffer to set the color of instances
  
  //Select our map object as a Selection object which will serve as our CoT
  let chart = anu.selectName('meshMap', scene);
  
  //Create instanced sphere meshes from our rootSphere as children of our CoT for each row of our data and set their visual encodings using method chaining
  let spheres = chart.bindInstance(rootSphere, data)
                     .positionX((d) => meshMap.projection([d.longitude, d.latitude])[0]) //The meshMap prefab object contains a projection() function that will convert
                     .positionZ((d) => meshMap.projection([d.longitude, d.latitude])[1]) //a longitude and latitude into the correct position on the meshMap
                     .setInstancedBuffer('color', new BABYLON.Color4(0,0,0,1));

  return scene;
}