Skip to main content
The Globe component is a highly customizable 3D interactive globe visualization built with Three.js and React Three Fiber. It supports various features like country polygons, arcs, points, labels, and atmospheric effects.

Features

  • Interactive 3D globe with smooth rotation and zoom controls
  • Customizable appearance (colors, lighting, atmosphere)
  • Support for GeoJSON data overlays
  • Animated arcs between points on the globe
  • Points of interest with labels
  • Atmospheric glow effects
  • Performance optimizations for large datasets

Installation

npm install three @react-three/fiber @react-three/drei three-globe

Basic Usage

import { Canvas } from '@react-three/fiber';
import { Globe } from './Globe';

function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <Canvas>
        <Globe />
      </Canvas>
    </div>
  );
}

Props

PropTypeDefaultDescription
globeConfigGlobeConfigSee belowConfiguration object for the globe’s appearance and behavior
dataPosition[][]Array of positions for arcs and points
isVisiblebooleantrueToggle visibility of the globe
controlsRefReact.RefObjectundefinedRef to access the camera controls
overlayGeoJsonanyundefinedGeoJSON data to overlay on the globe

GlobeConfig

PropertyTypeDefaultDescription
pointSizenumber1Size of points on the globe
globeColorstring"#1B1B1B"Base color of the globe
showAtmospherebooleantrueShow atmospheric glow
atmosphereColorstring"#ffffff"Color of the atmosphere
atmosphereAltitudenumber0.2Thickness of the atmosphere
emissivestring"#000000"Emissive color of the globe
emissiveIntensitynumber0.1Intensity of the emissive effect
shininessnumber0.9Shininess of the globe material
polygonColorstring"#1B1B1B"Color of country polygons
ambientLightstring"#ffffff"Ambient light color
directionalLeftLightstring"#ffffff"Left directional light color
directionalTopLightstring"#ffffff"Top directional light color
pointLightstring"#ffffff"Point light color
arcTimenumber2000Duration of arc animations in ms
arcLengthnumber0.9Length of arcs as a percentage
ringsnumber1Number of rings to display
maxRingsnumber3Maximum number of rings
initialPosition{ lat: number, lng: number }{ lat: 0, lng: 0 }Initial camera position
autoRotatebooleantrueEnable auto-rotation
autoRotateSpeednumber0.5Speed of auto-rotation
enableZoombooleantrueEnable zoom controls
zoomSpeednumber0.8Zoom speed
minDistancenumber200Minimum zoom distance
maxDistancenumber1000Maximum zoom distance
enableDampingbooleantrueEnable damping for smooth movement
dampingFactornumber0.2Damping factor
hexPolygonResolutionnumber3Resolution of hex polygons
maxPolygonFeaturesnumber100Maximum number of polygon features to render
maxDPRnumber1.5Maximum device pixel ratio
showArcsbooleantrueShow arcs between points
showPointsbooleantrueShow points on the globe
showLabelsbooleantrueShow labels for points
showRingsbooleantrueShow rings around points
autoRotateDisableDistancenumber300Disable auto-rotation when zoomed in closer than this distance
rotateSpeednumber1Base rotation speed
cursorSlowMultipliernumber0.5Multiplier for cursor movement speed when zoomed in

Examples

Basic Globe with Custom Colors

<Globe
  globeConfig={{
    globeColor: "#062056",
    showAtmosphere: true,
    atmosphereColor: "#FFFFFF",
    atmosphereAltitude: 0.15,
    polygonColor: "rgba(255,255,255,0.7)",
    showRings: true,
    rings: 3,
    maxRings: 3,
  }}
  data={[
    {
      order: 1,
      startLat: 40.7128,
      startLng: -74.0060,
      endLat: 51.5074,
      endLng: -0.1278,
      arcAlt: 0.2,
      color: "#FF6B6B"
    }
  ]}
/>

Interactive Globe with Click Events

function InteractiveGlobe() {
  const [clickedPoint, setClickedPoint] = useState(null);
  
  const handleGlobeClick = (event) => {
    // Get the clicked point in 3D space
    const point = event.point;
    // Convert to lat/lng
    const latLng = cartesianToLatLng(point.x, point.y, point.z);
    setClickedPoint(latLng);
  };

  return (
    <div>
      <Canvas onClick={handleGlobeClick}>
        <Globe 
          globeConfig={{
            // Your config
          }}
        />
      </Canvas>
      {clickedPoint && (
        <div style={{ position: 'absolute', top: 20, left: 20, background: 'white', padding: '10px' }}>
          Clicked at: {clickedPoint.lat.toFixed(2)}, {clickedPoint.lng.toFixed(2)}
        </div>
      )}
    </div>
  );
}

Performance Considerations

  • For large datasets, consider using maxPolygonFeatures to limit the number of rendered polygons
  • Adjust hexPolygonResolution to balance quality and performance
  • Use maxDPR to control rendering quality on high-DPI displays
  • Disable unnecessary features (arcs, rings, labels) when not needed

Dependencies

  • three
  • @react-three/fiber
  • @react-three/drei
  • three-globe

Centroid Worker

The Globe component uses a Web Worker (centroid.worker.ts) to efficiently calculate centroids for GeoJSON features in a background thread, ensuring smooth performance even with complex geographical data.

Purpose

The centroid worker is responsible for:
  • Processing GeoJSON features (Polygon and MultiPolygon) in parallel
  • Calculating the centroid (geometric center) for each feature
  • Extracting relevant properties for labeling
  • Maintaining performance by offloading CPU-intensive calculations from the main thread

How It Works

  1. Input: The worker receives GeoJSON features with the following structure:
    {
      features: Array<{
        type: 'Feature',
        geometry: {
          type: 'Polygon' | 'MultiPolygon',
          coordinates: number[][][] | number[][][][]
        },
        properties: {
          name?: string,
          ADMIN?: string,
          iso_a2?: string,
          count?: number,
          [key: string]: any
        }
      }>,
      maxLabels?: number // Optional limit on number of labels to return
    }
    
  2. Processing:
    • For each feature, it extracts the name from various possible property fields (name, ADMIN, iso_a2)
    • Calculates the centroid by averaging all coordinate points of the polygon’s exterior ring
    • Handles both Polygon and MultiPolygon geometry types
    • Preserves the count property if present for data visualization
  3. Output: Returns an array of label objects:
    Array<{
      lat: number,      // Latitude of the centroid
      lng: number,      // Longitude of the centroid
      country: string,  // Extracted name of the region
      count?: number    // Optional count from properties
    }>
    

Performance Considerations

  • Web Worker: Runs in a separate thread, preventing UI freezes during calculations
  • Efficient Calculation: Uses simple averaging for centroids, which is fast and sufficient for most visualization purposes
  • Error Handling: Includes try-catch blocks to handle malformed GeoJSON gracefully
  • Debugging: Contains debug logging that can be enabled for troubleshooting

Integration with Globe Component

The Globe component uses this worker to position labels and interactive elements on the globe. The centroids help in:
  • Placing country/region labels at appropriate positions
  • Positioning interactive markers for regions
  • Enabling hover and click interactions with geographical features

Customization

You can extend the worker to:
  1. Add more sophisticated centroid calculation methods
  2. Include additional properties in the output
  3. Implement custom filtering or transformation of features
  4. Add support for additional GeoJSON geometry types

HubSpot GeoJSON Integration

The Globe component can visualize GeoJSON data from HubSpot’s API to create interactive heatmaps. Here’s how to integrate it:

Fetching and Using HubSpot GeoJSON

  1. First, fetch the GeoJSON data from the HubSpot API:
import { useEffect, useState } from 'react';

function HubSpotGlobe() {
  const [geoJsonData, setGeoJsonData] = useState(null);

  useEffect(() => {
    const fetchGeoData = async () => {
      try {
        const response = await fetch(
          'https://yourdomain.com/hubspot/stats/geo-map/2025-11-01/2025-11-15',
          {
            headers: {
              'Authorization': 'Bearer YOUR_TOKEN_HERE'
            }
          }
        );
        
        if (!response.ok) throw new Error('Failed to fetch GeoJSON data');
        
        const data = await response.json();
        setGeoJsonData(data.data.geojson);
      } catch (error) {
        console.error('Error fetching GeoJSON:', error);
      }
    };

    fetchGeoData();
  }, []);

  // Function to determine color based on incident count
  const getColorForCount = (count) => {
    // Customize these thresholds based on your data
    if (count > 15) return '#ff0000';  // Red for high density
    if (count > 5) return '#ffa500';   // Orange for medium-high
    if (count > 0) return '#ffff00';   // Yellow for low
    return '#00ff00';                  // Green for none
  };

  // Process GeoJSON features to add custom styling
  const processedGeoJson = geoJsonData ? {
    ...geoJsonData,
    features: geoJsonData.features.map(feature => ({
      ...feature,
      properties: {
        ...feature.properties,
        // Add custom styling based on your data
        color: getColorForCount(feature.properties.count),
        // Add tooltip content
        tooltip: `${feature.properties.name}: ${feature.properties.count} incidents`,
      }
    }))
  } : null;

  return (
    <div style={{ width: '100%', height: '100vh' }}>
      <Canvas>
        <Globe 
          overlayGeoJson={processedGeoJson}
          globeConfig={{
            // Customize globe appearance
            globeColor: '#0a1128',
            showAtmosphere: true,
            atmosphereColor: 'rgba(0, 100, 255, 0.2)',
            polygonColor: (feature) => feature?.properties?.color || '#1B1B1B',
            // Other config options...
          }}
        />
      </Canvas>
    </div>
  );
}

Customizing the Heatmap

You can customize how the GeoJSON data appears on the globe using these approaches:
  1. Color Scaling: Modify the getColorForCount function to adjust the color thresholds based on your data distribution.
  2. Tooltips: The example includes a basic tooltip showing the region name and incident count. You can enhance this with more details as needed.
  3. Interactive Features: Add click handlers to regions to show detailed information or navigate to related views.
  4. Animation: Add transitions when data updates to create smooth visualizations.

Performance Considerations

  • For large datasets, consider aggregating data at a higher geographic level (e.g., country instead of state).
  • Use maxPolygonFeatures to limit the number of features rendered at once.
  • Implement data caching to avoid unnecessary API calls.
The component includes several utility functions in the functions directory:
  • cartesianToLatLng: Convert 3D coordinates to latitude/longitude
  • computeCentroidsSync: Calculate centroids for GeoJSON features
  • coordHashForFeature: Generate a hash for feature coordinates
  • darkenColor: Darken a hex or RGB color
  • genRandomNumbers: Generate random numbers within a range
  • getFeatureId: Get a unique ID for a GeoJSON feature
  • haversineKm: Calculate distance between two points using Haversine formula
  • hexToRgb: Convert hex color to RGB
  • latLngToCartesian: Convert latitude/longitude to 3D coordinates
  • pointInPolygonFeature: Check if a point is inside a polygon feature
  • pointInRing: Check if a point is inside a ring

Browser Support

The Globe component uses WebGL for rendering and requires a modern browser with WebGL support.