mmz
mmz

Reputation: 1151

Rotate Globe.gl globe (with react-three-fiber or otherwise) in React

I'm following this example from Globe.gl to achieve the following output. I want to rotate this globe using react-three-fiber, namely using the approach from this example.

Here is my Globe.js:

import React from "react";
import * as d3 from "d3-dsv";
import indexBy from "index-array-by";
import Globe from "react-globe.gl";

import earth from "../../public/images/earth-dark.png";
import background from "../../public/images/globeBackground.png";

function GlobeHome() {
  const { useState, useEffect, useRef } = React;
  const globeEl = useRef();
  const [airports, setAirports] = useState([]);
  const [routes, setRoutes] = useState([]);
  
  const COUNTRY = "United States";
  const OPACITY = 0.125;
  const airportParse = ([
    airportId,
    name,
    city,
    country,
    iata,
    icao,
    lat,
    lng,
    alt,
    timezone,
    dst,
    tz,
    type,
    source,
  ]) => ({
    airportId,
    name,
    city,
    country,
    iata,
    icao,
    lat,
    lng,
    alt,
    timezone,
    dst,
    tz,
    type,
    source,
  });
  const routeParse = ([
    airline,
    airlineId,
    srcIata,
    srcAirportId,
    dstIata,
    dstAirportId,
    codeshare,
    stops,
    equipment,
  ]) => ({
    airline,
    airlineId,
    srcIata,
    srcAirportId,
    dstIata,
    dstAirportId,
    codeshare,
    stops,
    equipment,
  });

  useEffect(() => {
    Promise.all([
      fetch(
        "https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat"
      )
        .then((res) => res.text())
        .then((d) => d3.csvParseRows(d, airportParse)),
      fetch(
        "https://raw.githubusercontent.com/jpatokal/openflights/master/data/routes.dat"
      )
        .then((res) => res.text())
        .then((d) => d3.csvParseRows(d, routeParse)),
    ]).then(([airports, routes]) => {
      const byIata = indexBy(airports, "iata", false);

      const filteredRoutes = routes
        .filter(
          (d) =>
            byIata.hasOwnProperty(d.srcIata) && byIata.hasOwnProperty(d.dstIata)
        )
        .filter((d) => d.stops === "0")
        .map((d) =>
          Object.assign(d, {
            srcAirport: byIata[d.srcIata],
            dstAirport: byIata[d.dstIata],
          })
        )
        .filter(
          (d) =>
            d.srcAirport.country === COUNTRY && d.dstAirport.country !== COUNTRY
        );

      setAirports(airports);
      setRoutes(filteredRoutes);
    });
  }, []);

  useEffect(() => {
    globeEl.current.pointOfView({ lat: 42, lng: -71, altitude: 2 });
  }, []);

  return (
    <Globe
      ref={globeEl}
      width={1000}
      height={1000}
      showGlobe={true}
      globeImageUrl={earth}
      backgroundImageUrl={background}
      arcsData={routes}
      arcStartLat={(d) => +d.srcAirport.lat}
      arcStartLng={(d) => +d.srcAirport.lng}
      arcEndLat={(d) => +d.dstAirport.lat}
      arcEndLng={(d) => +d.dstAirport.lng}
      arcDashLength={0.25}
      arcDashGap={1}
      arcDashInitialGap={() => Math.random()}
      arcDashAnimateTime={4000}
      arcColor={(d) => [
        `rgba(48, 64, 77, ${OPACITY})`,
        `rgba(191, 204, 214, ${OPACITY})`,
      ]}
      arcsTransitionDuration={0}
      pointsData={airports}
      pointColor={() => "white"}
      pointAltitude={0}
      pointRadius={0.03}
      pointsMerge={true}
    />
  );
}

export default GlobeHome;

which I then import into Boxes.js:

import React, { useRef, useState } from "react";
import { Canvas, useFrame } from "react-three-fiber";
import Globe from './Globe'

function Box(props) {
  // This reference will give us direct access to the mesh
  const mesh = useRef();
  // Set up state for the hovered and active state
  const [hovered, setHover] = useState(false);
  const [active, setActive] = useState(false);
  // Rotate mesh every frame, this is outside of React without overhead
  useFrame(() => {
    mesh.current.rotation.x = mesh.current.rotation.y += 0.01;
  });
  return (
    <mesh
      {...props}
      ref={mesh}
    >
      <Globe />
    </mesh>
  );
}

export default function App() {
  return (
    <Canvas>
      <ambientLight intensity={0.5} />
      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
      <pointLight position={[-10, -10, -10]} />
      <Box />
    </Canvas>
  );
}

Using this approach gives me the following error:

Uncaught "Div" is not part of the THREE namespace! Did you forget to extend it?

Thoughts on de-bugging? Is there a better way of rotating this Globe.gl globe?

Upvotes: 1

Views: 3890

Answers (3)

Ilir
Ilir

Reputation: 656

Into you Globe.js component, inside useEffect() add this: globeEl.current.controls().autoRotate = true;

Regarding your error, its something else. You need to read this

Upvotes: 2

Pabin Dhami
Pabin Dhami

Reputation: 1

The latest version of @react-three/fiber does not support shadowMap as the prop in Canvas instead we must use shadows props in the canvas. But the shadow can also be achieved using the element in jsx which is imported from "@react-three/dier" : https://github.com/pmndrs/drei#contactshadows

 <canvas shadows>
    {/*This is the shadow generating element imported from @react-three/drei */}
    <ContactShadows opacity={1} scale={10} blur={1} far={10} resolution={256} color="#000000" />

{/*You must have shadow generating and shadow receiving objects but not the ContactShadows element to support shadows genereated by lights*/}
    </canvas>

Upvotes: 0

Anthony
Anthony

Reputation: 21

I ran into this issue as well. The problem is that you're passing the Globe component as a property of the Box component which is a mesh. The Box component is then in the Canvas component. This library requires that you place the Globe component as a standard React component outside of the Canvas component. I was able to fix the issue in my logic by changing the App component to just return <Globe ...> with its properties and nothing else.

Upvotes: 1

Related Questions