Headquarters

Icosphere with a geojson dataset attached, similar to the Mars globe from globekit.co.

Each geojson point is clickable and causes a callout to appear. Each headquarter is rendered using the GlobeKit renderer, and there is a single DOM callout element at a time. This is a great option for high data volumes where performance is key.

Demonstrates:

  • Icosphere
  • Low Volume Data Viz with Texture mapping
  • Interactive
  • Single Arc drawing
  • Callout Manager
  • Loading data from a json file

Instructions

Step 1: Setup a GlobeKitView

import {GlobeKitView, PointGlobe, Points} from 'path/to/globekit.esm.js';

const apiKey = '{YOUR_API_KEY}';

const canvas = document.getElementById('globekit-canvas');

const gkOptions = {
  apiKey,
  wasmPath: '{path/to/gkweb_bg.wasm}',
  attributes: {
    alpha: false,
  },
};

const gkview = new GlobeKitView(canvas, gkOptions);

Step 2: Setup the Icosphere

Lets get an Icosphere up on screen.

icosphere = new Icosphere('{Path/to/image/src}');
icosphere.setInteractive(true, true, false);
gkview.addDrawable(icosphere, () => {
  gkview.startDrawing();
});

Done.

Step 3: Put a basic pin at the Home Base

pin = new QuadNormal({
  texture: './assets/pin.png',
});

// Set the scale of the pin
pin.nScale = 1.5;

// Place pin at these lat/lon
pin.setPoint(41, -111);
gkview.addDrawable(pin);

Step 4: Add textured Points

// Points data object
points = new Points({
  useTexture: true,
  texture: './assets/target.png',
});
points.setInteractive(true, true, false);

// This value sets the max distance to trigger a selection in km
points.maxSelectionDistance = 200;

// Define the points transform
points.transform = (element, point) => {
  point.size = 24;
  return point;
};

// Get data for the points
fetch('./data/offices.json')
  .then((response) => response.json())
  .then((data) => {
    points.addGeojson(data);
  });
gkview.addDrawable(points);

Step 5: Draw an Arc onSelection

Add an empty Arc drawable at the bottom of the file.

const arcs = new Arc();
gkview.addDrawable(arcs);

Define the onSelection Callback on the GlobeKitView object.

// onSelection gets called when the globe reports a selection event
gkview.onSelection = (list) => {
  // Uncomment this line to see the list object
  // console.log(list);

  list.drawables.forEach((el) => {
    // This gets run if the points object is selected
    if (el.obj.id === points.id) {
      if (el.selection !== undefined) {
        const sel = el.selection[0][0];

        // Clear all effects
        arcs.removeAllArcs();

        // Create an arc from airport to resort
        const arcParams = {
          from: [sel.lat, sel.lon],
          to: [41, -111], 
          startColor: '#ffffff',
          startAlpha: 1.0,
          midWidth: 4,
        };

        // Draw the arc to the screen in 1 second
        arcs.addArc(arcParams, 1000);
      } else {
        // Remove all effects if no selection
        arcs.removeAllArcs();
      }
    }
  });
};

Now, when you click on a point, an Arc should draw from the selected point to Salt Lake City, UT.

Step 6: Setup the CalloutManager

// Callout manager moves callouts to keep them attached to their points
// The CalloutManager uses a `div' to define the bounds for the callouts
calloutManager = new CalloutManager(document.getElementById('callout-manager'));
gkview.registerCalloutManager(calloutManager);

// This gets called when the calloutManager removes a callout, i.e. when it rotates behind the globe.
calloutManager.onAutoRemove = (def) => {
  arcs.removeAllArcs();
};

The CalloutManager keeps track of registered locations on the globe and provides pixel coordinates to a DOM element that is generated by a Callout sub-class. The area that the Callouts will be constrained to is defined by a div that is passed into the CalloutManager constructor.

Using a virtual DOM could cause issues with the CalloutManager. React and Vue implementations are in progress. Please reach out with any questions or issues.

Step 7: Define a Callout

In a new file, OfficeCallout.js, define a class that displays the data from the Points data json.

import { Callout } from '{path/to/globekit.esm.js';

/**
  * This is the definition of the callout that appears over the selected airport
  */
class OfficeCallout extends Callout {
  createElement() {
    // Uncomment this line then click on an airport to see the data object;
    // console.log(this.definition.data);

    // Here is the html element that will be attached to the coord
    const div = document.createElement('div');
    div.className = 'office-callout';
    div.innerHTML = `<div class="callout-container">
      <h3>${this.definition.data.name}</h3>
      <table>
        <tbody>
          <tr>
            <td>Employees:</td>
            <td>${this.definition.data.employees}</td>
          </tr>
          <tr>
            <td>Bigfoot Tours Sold:</td>
            <td>${this.definition.data.bigfoot_tours_sold}</td>
          </tr>
        </tbody>
      </table>
    </div>`;
    return div;
  }

  // This function sets offsets for the htmlElement from the lat/lon coord
  setPosition(position) {
    const nx = position.screen.x - 85;
    const ny = position.screen.y - 123;
    this.element.style.transform = `translate(${nx.toFixed(1)}px, ${ny.toFixed(0)}px)`;
  }
}

export { OfficeCallout };

Note the use of this.definition.data.* to access any data that we would like to display. You will see in the next step how to pass this data to the Callout.

Step 8: Show Callout onSelection

Import the callout into the main js file.

import { OfficeCallout } from './officecallout.js';

Update the onSelection to look like this.

// onSelection gets called when the globe reports a selection event
gkview.onSelection = (list) => {
  // Uncomment this line to see the list object
  // console.log(list);

  list.drawables.forEach((el) => {
    // This gets run if the points object is selected
    if (el.obj.id === points.id) {
      if (el.selection !== undefined) {
        const sel = el.selection[0][0];

        // Clear all effects
        calloutManager.removeAllCallouts();
        arcs.removeAllArcs();

        // Add the callout at location with properties
        calloutManager.addCallout(new CalloutDefinition(sel.lat, sel.lon, OfficeCallout, sel.properties));

        // Create an arc from airport to resort
        const arcParams = {
          from: [sel.lat, sel.lon],
          to: [41, -111],
          startColor: '#ffffff',
          startAlpha: 1.0,
          midWidth: 4,
        };

        // Draw the arc to the screen in 1 second
        arcs.addArc(arcParams, 1000);
      } else {
        // Remove all effects if no selection
        arcs.removeAllArcs();
        calloutManager.removeAllCallouts();
      }
    }
  });
};

The addCallout line is where we create a new instance of a callout at the specified location. The CalloutDefinition class wraps our OfficeCallout in the required machinery for the manager. The sel.properties parameter is the actual data that will be rendered inside the callout.

Conclusion

Now you should have an Icosphere that you can interact with on your screen. Now you can tweak the look and functionality to fit your design.

Source

Source is available in the examples/headquarters folder in the GlobeKit package