diff --git a/.github/workflows/sync.yaml b/.github/workflows/sync.yaml
index 61ea0cf..93c24a7 100644
--- a/.github/workflows/sync.yaml
+++ b/.github/workflows/sync.yaml
@@ -37,6 +37,11 @@ jobs:
cd ./classes/week-8/animated-highlights-r3f
npm install
npm run build
+ - name: Build Week 10 - Data, Controls, Interactions
+ run: | # Week 8 - Animated Highlights
+ cd ./classes/week-10/data-controls-interactions-r3f
+ npm install
+ npm run build
# Minimal test of the GitHub Action
# (only runs on pushes to the `main` branch)
# Glitch Project: https://glitch.com/edit/#!/github-action
diff --git a/classes/week-10/data-controls-interactions-r3f/.gitignore b/classes/week-10/data-controls-interactions-r3f/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/classes/week-10/data-controls-interactions-r3f/README.md b/classes/week-10/data-controls-interactions-r3f/README.md
new file mode 100644
index 0000000..1868345
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/README.md
@@ -0,0 +1 @@
+# ITP Visual Journalism Fall 2023 - Interactions & Controls & Data
diff --git a/classes/week-10/data-controls-interactions-r3f/index.html b/classes/week-10/data-controls-interactions-r3f/index.html
new file mode 100644
index 0000000..f290727
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ ITP Visual Journalism Fall 2023 - Week 10
+
+
+
+
+
+
diff --git a/classes/week-10/data-controls-interactions-r3f/package.json b/classes/week-10/data-controls-interactions-r3f/package.json
new file mode 100644
index 0000000..6f1c762
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "data-controls-interactions-r3f",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "start": "vite",
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@react-three/drei": "^9.80.4",
+ "@react-three/fiber": "^8.13.7",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "three": "^0.155.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.2.15",
+ "@types/react-dom": "^18.2.7",
+ "@types/three": "^0.155.0",
+ "@vitejs/plugin-react": "^4.0.3",
+ "vite": "^4.3.8"
+ },
+ "engines": {
+ "node": "16.x"
+ }
+}
diff --git a/classes/week-10/data-controls-interactions-r3f/public/earth.jpg b/classes/week-10/data-controls-interactions-r3f/public/earth.jpg
new file mode 100644
index 0000000..73ef57f
Binary files /dev/null and b/classes/week-10/data-controls-interactions-r3f/public/earth.jpg differ
diff --git a/classes/week-10/data-controls-interactions-r3f/src/App.css b/classes/week-10/data-controls-interactions-r3f/src/App.css
new file mode 100644
index 0000000..8523551
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/src/App.css
@@ -0,0 +1,18 @@
+body {
+ /* Reseting the default here */
+ margin: 0;
+}
+
+#article_wrapper {
+ position: relative;
+ background-color: grey;
+ height: 100vh; /* Length of the interactive scroll article */
+}
+
+#canvas_wrapper {
+ position: -webkit-sticky; /* Safari */
+ position: sticky;
+ top: 0;
+ width: 100vw;
+ height: 100vh;
+}
diff --git a/classes/week-10/data-controls-interactions-r3f/src/App.jsx b/classes/week-10/data-controls-interactions-r3f/src/App.jsx
new file mode 100644
index 0000000..96aaad3
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/src/App.jsx
@@ -0,0 +1,15 @@
+import Scene from "./Scene";
+
+// We add a CSS file here so we can style components
+import "./App.css";
+
+function App() {
+ return (
+
+ {/* 3D scene container */}
+
+
+ );
+}
+
+export default App;
diff --git a/classes/week-10/data-controls-interactions-r3f/src/Earth.jsx b/classes/week-10/data-controls-interactions-r3f/src/Earth.jsx
new file mode 100644
index 0000000..6e588f8
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/src/Earth.jsx
@@ -0,0 +1,13 @@
+import { useTexture } from "@react-three/drei";
+
+function Earth() {
+ const earthTexture = useTexture("/earth.jpg");
+ return (
+
+
+
+
+ );
+}
+
+export default Earth;
diff --git a/classes/week-10/data-controls-interactions-r3f/src/LocationMarker.jsx b/classes/week-10/data-controls-interactions-r3f/src/LocationMarker.jsx
new file mode 100644
index 0000000..a1594c6
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/src/LocationMarker.jsx
@@ -0,0 +1,43 @@
+import { useState } from "react";
+import { Html } from "@react-three/drei";
+
+function LocationMarker({ position, name }) {
+ const [isExpanded, setIsExpanded] = useState(false);
+ return (
+ <>
+ {
+ if (!isExpanded) setIsExpanded(true);
+ }}
+ onPointerLeave={() => {
+ if (isExpanded) setIsExpanded(false);
+ }}
+ position={position}
+ >
+ {isExpanded && (
+
+
+
{name}
+
+ {name} - here we can add more details dynamically from the data
+ - or by stitching other data!
+
+
+
+ )}
+
+
+
+ >
+ );
+}
+
+export default LocationMarker;
diff --git a/classes/week-10/data-controls-interactions-r3f/src/Scene.jsx b/classes/week-10/data-controls-interactions-r3f/src/Scene.jsx
new file mode 100644
index 0000000..dbbf125
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/src/Scene.jsx
@@ -0,0 +1,90 @@
+import { useEffect, useState } from "react";
+import { Vector3 } from "three";
+import { Canvas } from "@react-three/fiber";
+import {
+ Environment,
+ OrbitControls,
+ PerspectiveCamera,
+} from "@react-three/drei";
+
+import LocationMarker from "./LocationMarker";
+import Earth from "./Earth";
+
+// This function converts geographic coordinates (latitude and longitude) into a 3D vector.
+function latLongToVector3(lat, lon, radius) {
+ // Convert latitude and longitude from degrees to radians
+ const phi = (lat * Math.PI) / 180;
+ const theta = ((lon - 180) * Math.PI) / 180;
+
+ // Calculate the x, y, and z coordinates using the spherical to Cartesian coordinates conversion formulas
+ const x = -(radius * Math.cos(phi) * Math.cos(theta));
+ const y = radius * Math.sin(phi);
+ const z = radius * Math.cos(phi) * Math.sin(theta);
+
+ return new Vector3(x, y, z);
+}
+
+function Scene() {
+ // We use state to store our data points
+ const [dataPoints, setDataPoints] = useState([]);
+
+ // We fetch our data and do any pre-processing needed to organize it
+ useEffect(() => {
+ fetch(
+ "https://raw.githubusercontent.com/eesur/country-codes-lat-long/master/country-codes-lat-long-alpha3.json"
+ ).then((res) => {
+ res.json().then((data) => {
+ const { ref_country_codes } = data;
+
+ const filteredData = ref_country_codes.filter(
+ (item) => item.latitude && item.longitude
+ );
+ const mappedData = filteredData.map(
+ ({ longitude, latitude, country }) => {
+ return {
+ position: latLongToVector3(latitude, longitude, 1),
+ name: country,
+ };
+ }
+ );
+ setDataPoints(mappedData);
+ });
+ });
+ }, []);
+
+ return (
+
+
+
+ );
+}
+
+export default Scene;
diff --git a/classes/week-10/data-controls-interactions-r3f/src/main.jsx b/classes/week-10/data-controls-interactions-r3f/src/main.jsx
new file mode 100644
index 0000000..35c73b7
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/src/main.jsx
@@ -0,0 +1,5 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App.jsx";
+
+ReactDOM.createRoot(document.getElementById("root")).render();
diff --git a/classes/week-10/data-controls-interactions-r3f/vite.config.js b/classes/week-10/data-controls-interactions-r3f/vite.config.js
new file mode 100644
index 0000000..e46c617
--- /dev/null
+++ b/classes/week-10/data-controls-interactions-r3f/vite.config.js
@@ -0,0 +1,10 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ server: {
+ port: 3000,
+ },
+ plugins: [react()],
+});