Skip to content

Commit

Permalink
Style tooltip. Correctly align markers and fix rotation
Browse files Browse the repository at this point in the history
  • Loading branch information
Bluesmile82 committed Oct 16, 2024
1 parent 669f328 commit f982925
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 113 deletions.
17 changes: 17 additions & 0 deletions public/images/home-legend-1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions public/images/home-legend-2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions public/images/home-legend-3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions public/images/home-legend-4.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import { CameraControls } from "@react-three/drei";
import { useRef } from "react";
import { convertLatLonToGlobalPosition } from "@/lib/globe-utils";
import type { MarkerType } from "./marker";
import { Group } from "three";

export const Camera = ({ marker, disabled = false }: {
export const Controls = ({ marker, disabled = false, groupRef, resetSelectedMarker }: {
marker: MarkerType | undefined
disabled: boolean
groupRef: React.MutableRefObject<Group>
resetSelectedMarker: () => void
}) => {
const cameraControlsRef = useRef<CameraControls>(null!);
const [resetControls, setResetControls] = useState<boolean>(true);
Expand All @@ -18,36 +21,36 @@ export const Camera = ({ marker, disabled = false }: {
}
}, [marker]);

const resetPosition = () => {
groupRef.current.rotation.y = 0;
cameraControlsRef.current.setPosition(0, 1, 4.9, true);
cameraControlsRef.current.setTarget(0, 0, 0, true);
};

useEffect(() => {
if (cameraControlsRef.current) {
if (marker !== undefined) {
const [x, y, z] = convertLatLonToGlobalPosition(marker.lat, marker.lng, 2);
cameraControlsRef.current.disconnect();
groupRef.current.rotation.y = 0;
cameraControlsRef.current.setPosition(x, y, z, true);
}

if (resetControls) {
cameraControlsRef.current.connect(canvasElement);
cameraControlsRef.current.setPosition(0, 1, 4.9, true);
cameraControlsRef.current.setTarget(0, 0, 0, true);
resetPosition();
setResetControls(false);
}
}
}, [marker, resetControls, canvasElement]);

useEffect(() => {
if (disabled && cameraControlsRef.current) {
cameraControlsRef.current.connect(canvasElement);
cameraControlsRef.current.setPosition(0, 1, 2, true);
cameraControlsRef.current.setTarget(0, 0, 0, true);
setResetControls(true);
if (disabled) {
resetSelectedMarker();
resetPosition();
}
}, [disabled, cameraControlsRef.current]);

if (disabled) {
return null;
}

return (
<CameraControls
ref={cameraControlsRef}
Expand Down
63 changes: 63 additions & 0 deletions src/components/globe-map/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { MarkerType } from "./marker";

export const markers: MarkerType[] = [
{ id: "Amazonia", lat: 1.0, lng: -72.65 },
{ id: "Hurricane", lat: 31.87, lng: -70.0 },
{ id: "Temperature", lat: 38.35, lng: -3.67 },
{ id: "Sea surface temp", lat: -111.0, lng: 0.75 },
];

export const popupContent = [
{
id: "Amazonia",
title: "Amazon. Brazil",
subtitle: "Diurnal precipitation cycle",
description:
"The diurnal precipitation cycle over the Amazon rainforest highlights the complex interactions and feedbacks between atmosphere and land. The diurnal rainfall, maintained by moisture inflow from the Atlantic ocean into the Amazon basin, arises due to evapotranspiration and moisture recycling from the dense forest. This self-sustained cycle is in turn driven by sharp contrasts in temperature between day and night.",
video: "/iberia_hist_scenario.mp4",
legend: {
image: "/images/home-legend-1.svg",
low: "0.0 m³/m²",
high: "0.1 m³/m²",
},
},
{
id: "Hurricane",
title: "North Atlantic Ocean",
subtitle: "Tropical cyclone track",
description:
"Tropical cyclones in the North Atlantic Ocean typically form near the equator and propogate westward, often curving northward as they approach the Caribbean or the east coast of the US. These powerful storms are steered by trade winds and ocean currents, impacting coastal regions with strong winds, heavy rainfall, and storm surges.",
video: "/iberia_hist_scenario.mp4",
legend: {
image: "/images/home-legend-2.svg",
low: "0%",
high: "100%",
},
},
{
id: "Temperature",
title: "Pyrenees. Iberian Peninsula",
subtitle: "Diurnal temperature cycle",
description:
"The diurnal temperature cycle over the Pyrenees in the Iberian Peninsula describes the significant temperature fluctuations between day and night. During the day, sunlight heats the region, while at night, temperatures drop rapidly due to clear skies and high altitude, creating a marked difference in daily and nightly temperatures.",
video: "/iberia_hist_scenario.mp4",
legend: {
image: "/images/home-legend-3.svg",
low: "4º",
high: "46º",
},
},
{
id: "Sea surface temp",
title: "Pacific Ocean. Ecuador",
subtitle: "Ocean eddies and currents",
description:
"Ocean eddies and currents off the coast of Ecuador in the Pacific Ocean play a key role in shaping local marine ecosystems. The warm waters of the equatorial current interact with cooler, nutrient-rich upwellings (deep water), creating swirling currents, known as eddies, that support diverse marine life and influence weather patterns.",
video: "/iberia_hist_scenario.mp4",
legend: {
image: "/images/home-legend-4.svg",
low: "19º",
high: "31º",
},
},
];
46 changes: 46 additions & 0 deletions src/components/globe-map/globe-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Globe } from './globe';
import Marker from './marker';
import { useFrame } from '@react-three/fiber';
import type { markers as MarkerType } from './data';
import { Group } from 'three';

const GlobeGroup = ({ hasMarkers, markers, selectedMarker, setSelectedMarker, rotate, videoMaterial, groupRef }:
{
hasMarkers: boolean,
markers: typeof MarkerType,
selectedMarker: number | null,
setSelectedMarker: (index: number | null) => void,
rotate: boolean,
videoMaterial: string,
groupRef: React.MutableRefObject<Group>
}
) => {

useFrame(() => {
if (rotate && groupRef.current) {
groupRef.current.rotation.y += 0.01;
}
});

return (
<group position={[0, 0, 0]} ref={groupRef}>
<Globe videoMaterial={videoMaterial} />
{
hasMarkers && markers.map((marker, index) => (
<Marker
key={index}
index={markers.indexOf(marker)}
id={marker.id}
isSelected={selectedMarker === markers.indexOf(marker)}
setSelectedMarker={setSelectedMarker}
lat={marker.lat}
lng={marker.lng}
>
Click to explore the phenomenon
</Marker>
))
}
</group >
);
}
export default GlobeGroup;
16 changes: 3 additions & 13 deletions src/components/globe-map/globe.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useTexture, useVideoTexture } from "@react-three/drei";
import { Suspense, useRef } from "react";
import { Mesh } from "three";
import { useFrame } from '@react-three/fiber';
import { Suspense } from "react";


function VideoMaterial({ url }: { url: string }) {
const texture = useVideoTexture(url)
Expand All @@ -13,21 +12,12 @@ function FallbackMaterial({ url }: { url: string }) {
return <meshStandardMaterial map={texture} toneMapped={false} />
}

export const Globe = ({ rotate = false, videoMaterial }: {
rotate?: boolean;
export const Globe = ({ videoMaterial }: {
videoMaterial: string;
}) => {
const sphereMeshRef = useRef<Mesh>(null!);

useFrame(() => {
if (rotate && sphereMeshRef.current) {
sphereMeshRef.current.rotation.y += 0.01;
}
});

return (
<mesh
ref={sphereMeshRef}
position={[0, 0, 0]}
>
<sphereGeometry args={[1, 64, 64]} />
Expand Down
47 changes: 17 additions & 30 deletions src/components/globe-map/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
'use client';
import { useState, useRef, useEffect, CSSProperties } from 'react';
import { useState, useRef, useEffect, CSSProperties, useCallback } from 'react';
import { Canvas } from '@react-three/fiber'
import { Camera } from './camera';
import { Globe } from './globe';
import Marker from './marker';
import type { MarkerType } from './marker';
import { Group } from "three";
import { Controls } from './controls';

const markers: MarkerType[] = [
{ id: "Paris", lat: 48.8575, lng: 2.3514 },
{ id: "Los Angeles", lat: 34.0522, lng: -118.2437 },
{ id: "Tokio", lat: 35.6895, lng: 139.6917 },
{ id: "New York", lat: 40.7128, lng: -74.0060 },
{ id: "São Paulo", lat: -23.5505, lng: -46.6333 },
{ id: "Sydney", lat: -33.8688, lng: 151.2093 },
{ id: "Cape Town", lat: -33.9249, lng: 18.4241 },
];
import { markers } from './data';

import GlobeGroup from './globe-group';

export default function GlobeMap({ videoMaterial, className, style, hasMarkers = false, rotate = false }:
{
Expand All @@ -26,7 +18,7 @@ export default function GlobeMap({ videoMaterial, className, style, hasMarkers =
}
) {
const [selectedMarker, setSelectedMarker] = useState<number | null>(null);

const groupRef = useRef<Group>(null!);
const marker = selectedMarker !== null ? markers[selectedMarker] : undefined;
const canvasRef = useRef<HTMLCanvasElement>(null);

Expand All @@ -47,6 +39,10 @@ export default function GlobeMap({ videoMaterial, className, style, hasMarkers =
};
}, []);

const resetSelectedMarker = useCallback(() => {
setSelectedMarker(null);
}, []);

return (
<>
<div
Expand All @@ -58,22 +54,13 @@ export default function GlobeMap({ videoMaterial, className, style, hasMarkers =
ref={canvasRef}
resize={{ scroll: false, debounce: { scroll: 0, resize: 0 } }}
>
<Camera marker={marker} disabled={!hasMarkers} />
<Controls marker={marker} disabled={!hasMarkers} groupRef={groupRef} resetSelectedMarker={resetSelectedMarker} />
<ambientLight intensity={7} />
<Globe videoMaterial={videoMaterial} rotate={rotate} />
{hasMarkers && markers.map((marker, index) => (
<Marker
key={index}
index={markers.indexOf(marker)}
id={marker.id}
isSelected={selectedMarker === markers.indexOf(marker)}
setSelectedMarker={setSelectedMarker}
lat={marker.lat}
lng={marker.lng}
>
Click to explore the phenomenon
</Marker>
))}
<GlobeGroup
groupRef={groupRef}
hasMarkers={hasMarkers} markers={markers} selectedMarker={selectedMarker} setSelectedMarker={setSelectedMarker} rotate={rotate}
videoMaterial={videoMaterial}
/>
</Canvas>
</div>
</>
Expand Down
Loading

0 comments on commit f982925

Please sign in to comment.