1

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();
      }
    });
  };

}
juntunen
  • 207
  • 3
  • 10
  • Hi! I’m maintaining the latest version of Threebox. Do you have a minimal reproducible example or a repo I could take a look at? – jscastro May 07 '21 at 14:16
  • 1
    @jscastro here is a minimal reproducible example in a repo: https://github.com/tom-juntunen/mapboxgl-angular-demo Please run npm install, npm install --production=false, and insert a valid mapbox access token into environments/environment.ts. ng serve should fire up on localhost and this undefined error can be seen in the browser developer console. – juntunen May 07 '21 at 18:16
  • I’ll check that out, never tried Threebox with Angular but I’m sure we can make it run as we did with React and Vue – jscastro May 07 '21 at 19:12
  • Thank you, I was having a hard time finding examples using Threebox with Angular, something with the structure of the framework and interaction with the 3D model object loader is causing this error that I cannot figure out. – juntunen May 07 '21 at 19:51

2 Answers2

1

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);
          });
shabi8
  • 55
  • 6
0

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.

woflszy
  • 1
  • 1