Why can't I load 3D models with Mapbox GL JS and Threebox with this Angular code

1.2k views Asked by At

Can anyone help me understand how I need to structure my code for this mapbox w/ threebox project in Angular?

I can't figure out why this code:

 this.tb.loadObj(options, function (model) {
     var house = model.setCoords(origin);
     this.tb.add(house);
});

is throwing the following error:

ERROR Error: Uncaught (in promise): ReferenceError: tb is not defined ReferenceError: tb is not defined

Even after seeming to recognize tb as defined when I run console.log statements within this code block. But then this error shows up right afterwards and my 3D model never loads.

Here is the full code for the component, any advice on how to solve this issue would be appreciated:

import { Component, OnInit } from '@angular/core';
import { environment } from '../../../environments/environment';

import {Threebox} from 'threebox-plugin';
import * as M from 'mapbox-gl';

@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
  
  /// default settings
  long = -122.4192;
  lat = 37.7793;
  map: M.Map;
  style = 'mapbox://styles/mapbox/light-v10';

  // data
  source: any;
  markers: any;

  // render
  tb: Threebox;

  constructor() { }

  ngOnInit(): void {
    (M as any).accessToken = environment.mapbox.accessToken;
    this.buildMap();
    
  this.map.on('style.load', this.onLoad.bind(this));

  this.map.on('load', (event)  => {
    /// register source
    this.map.addSource('localdata', {
      type: 'geojson',
      data: {
        type: 'FeatureCollection',
        features: [
        {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [this.long, this.lat]
          },
          properties: {
            title: 'Mapbox',
            description: 'San Francisco, California'
          }
        }]
      }
    });

    /// get source
    this.source = this.map.getSource('localdata')

    // markers
    this.source._data.features.forEach((marker) => {
      var lng = marker['geometry']['coordinates'][0]
      var lat = marker['geometry']['coordinates'][1]

      // create a HTML element for each feature
      var el = document.createElement('div');
      el.className = 'marker';

      // make a marker for each feature and add to the map
    new M.Marker({color: 'black'})
        .setLngLat([lng, lat])
        .addTo(this.map);
    });
  });

  // Add map controls
  this.map.addControl(new M.NavigationControl());
  this.map.on('mousemove', function (e) {
      document.getElementById('info').innerHTML =
      // e.point is the x, y coordinates of the mousemove event relative
      // to the top-left corner of the map
      // e.lngLat is the longitude, latitude geographical position of the event
      e.lngLat.lat.toFixed(6) + ', ' + e.lngLat.lng.toFixed(6) ;
      });
      
  };

    // functions
    buildMap() {
        this.map = new M.Map({
          container: 'map',
          style: 'mapbox://styles/mapbox/light-v10',
          zoom: 18,
          center: [this.long, this.lat],
          pitch: 60,
      });
    }
    onLoad(){
      this.map.addLayer({
        id: 'house',
        type: 'custom',
        // renderingMode: '3d',
  
        onAdd(map, mbxContext) {

          this.tb = new Threebox(
              map,
              mbxContext,
              { defaultLights: true }
          );
          
          //starting location for both map and eventual sphere
          var origin = [this.long, this.lat];
          var options = {
              // obj: 'src/assets/3d/house.gltf',
              obj: 'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
              type: 'gltf',
              scale: 1,
              units: 'meters',
              rotation: { x: 90, y: 0, z: 0 } //default rotation
          }
  
          this.tb.loadObj(options, function (model) {
            var house = model.setCoords(origin);
            this.tb.add(house);
          });
      },
      render: function (gl, matrix) {
        this.tb.update();
      }
    });
  };

}
2

There are 2 answers

0
woflszy On

It seems as window.tb solves that issue.

Initialise tb outside onAdd function.

this.tb = new Threebox(map, mbxContext, options);
window.tb = this.tb;

Now you can use either this.tb or window.tb because both are the same.

0
shabi8 On

tb needs to be global: https://github.com/jscastro76/threebox/blob/master/docs/Threebox.md#threebox-instance try:

(window as any).tb = new Threebox(map, mbxContext, {defaultLights: true});

and

window['tb'].loadObj(options, function (model) {
            var house = model.setCoords(origin);
            window['tb'].add(house);
          });