Skip to content

Commit

Permalink
Version 2 of globe visualiser
Browse files Browse the repository at this point in the history
  • Loading branch information
tanmaylaud committed Jul 27, 2020
1 parent e0aa141 commit fc46197
Show file tree
Hide file tree
Showing 17 changed files with 1,105 additions and 2,175 deletions.
743 changes: 683 additions & 60 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@
],
"main": "src/index.tsx",
"dependencies": {
"es6-tween": "5.5.11",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-globe": "4.0.0",
"react-scripts": "3.4.1",
"three": "0.116.1"
"d3": "^5.16.0",
"dayjs": "^1.8.29",
"globe.gl": "^2.12.2"
},
"devDependencies": {
"@types/d3": "^5.7.2",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.2",
"typescript": "3.3.3",
"gh-pages": "^2.2.0"
"gh-pages": "^2.2.0",
"typescript": "3.3.3"
},
"scripts": {
"predeploy": "npm run build",
Expand Down
167 changes: 61 additions & 106 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,122 +1,77 @@
import * as React from "react";
import ReactGlobe, { Marker } from "react-globe";

import markers from "./markers";
import markerRenderer from "./markerRenderer";
import worldTexture from "./assets/world.jpg";
import "./styles.css";
import { useState, useEffect } from "react";
import { Object3D } from "three";
import { initGlobe } from "./Globe";
import { GlobalCounts } from "./GlobalCounts";
import { Counter } from "./Counter";
import { Spinner } from "./Spinner";
import globalData from "./assets/global.json";

function getWindowDimensions() {
const { innerWidth: width, innerHeight: height } = window;
return {
width,
height,
};
}

export default function App() {
const [details, setDetails] = useState<any>(null);
const [isLoaded, onTextureLoaded] = useState(false);
const [cameraOptions, setCameraOptions] = useState({
maxDistanceRadiusScale: 100,
autoRotateSpeed: 0.1,
distanceRadiusScale: 100,
});

const [windowDimensions, setWindowDimensions] = useState(
getWindowDimensions()
);
const [totals, setTotals] = useState<number[]>([]);

function getTooltipContent(marker: Marker) {
return `Location: ${marker.Country} (Active Cases: ${marker.activeCases})`;
}

function onClickMarker(
marker: Marker,
markerObject?: Object3D,
event?: PointerEvent
) {
zoomOut();
setDetails(getTooltipContent(marker));
}

function onDefocus(previousCoordinates: any, event?: PointerEvent) {
setDetails(null);
}

const zoomOut = () => {
useEffect(() => initGlobe(), []);
useEffect(() => {
setTimeout(() => {
if (windowDimensions.width <= 500) {
setDetails(null);
}
let total: number[] = [];
total.push(GlobalCounts.totalConfirmed);
total.push(GlobalCounts.totalDeaths);
total.push(GlobalCounts.totalRecoveries);
total.push(GlobalCounts.totalActive);
setTotals(total);
}, 3000);
};

}, [GlobalCounts.set]);
return (
<React.Fragment>
<Spinner loaded={isLoaded} />
<div className="header1">COVID19 Globe Tracker</div>
<div className="header2">Active Cases</div>
<div className="globe">
<ReactGlobe
markers={markers}
markerOptions={{ renderer: markerRenderer }}
onDefocus={onDefocus}
onClickMarker={onClickMarker}
onMouseOverMarker={onClickMarker}
onMouseOutMarker={() => setDetails(null)}
cameraOptions={{
maxDistanceRadiusScale: 100,
autoRotateSpeed: 1.0,
distanceRadiusScale: 100,
}}
focusOptions={{
distanceRadiusScale: 30,
enableDefocus: true,
}}
globeOptions={{
texture: worldTexture,
glowColor: "red",
enableClouds: false,
}}
onTextureLoaded={() => onTextureLoaded(true)}
/>
{details && (
<div className="details">
<p> {details}</p>
</div>
)}
<div id="globeViz"></div>
<div className="top-info-container">
<div className="title">COVID-19</div>
<div className="title-desc">
Loading countries affected by the virus...
</div>
</div>
{isLoaded ? (
<div className="footer">
<Counter />
<div className="bottom-info-container">
<Spinner loaded={GlobalCounts.set} />
{GlobalCounts.set ? (
<>
<div
style={{ fontSize: "14px", color: "#ccd6f6", marginTop: "35px" }}
>
Total Counts <span className="updated"></span>
</div>
<div style={{ color: "#e6f1ff", padding: "0 5px" }}>
<span id="infected">
INFECTED:
<Counter count={totals[0]} />
</span>
<span id="deaths">
{" "}
• DEATHS:
<Counter count={totals[1]} />
</span>
<span id="recovered">
{" "}
• RECOVERED:
<Counter count={totals[2]} />
</span>
<span id="active">
{" "}
• ACTIVE:
<Counter count={totals[3]} />
</span>
</div>
</>
) : null}
<div style={{ marginTop: "5px" }}>
<a
href="https://github.com/tanmaylaud/covid19-globe-tracker"
rel="noopener noreferrer"
target="_BLANK"
style={{ color: "#ffffff", textDecoration: "none" }}
>
More Information
</a>
</div>
) : null}
</div>
</React.Fragment>
);
}

function Counter() {
const [total, setTotal] = useState(globalData.totalCount);
const [counter, setCounter] = useState(0);
useEffect(() => {
setTimeout(() => null, 5000);
}, []);
useEffect(() => {
if (counter != total) {
if (total - counter <= 1000) setCounter(counter + 1);
else if (counter < 10000) setCounter(counter + 1000);
else setCounter(counter + 500);
}
}, [counter]);

function numberWithCommas(x: number) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

return <p>{numberWithCommas(counter)}</p>;
}
9 changes: 9 additions & 0 deletions src/Constants.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const GLOBE_IMAGE_URL =
"//cdn.jsdelivr.net/npm/three-globe/example/img/earth-dark.jpg";
export const BACKGROUND_IMAGE_URL =
"//cdn.jsdelivr.net/npm/three-globe/example/img/night-sky.png";
export const GEOJSON_URL =
"https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson";
export const CASES_API = "https://covid3d-backend.now.sh";

export const FLAG_ENDPOINT = "https://corona.lmao.ninja/assets/img/flags";
24 changes: 24 additions & 0 deletions src/Counter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import { useEffect, useState } from "react";

interface CounterProps {
count: number;
}

export function Counter({ count }: CounterProps) {
const [counter, setCounter] = useState(0);

useEffect(() => {
if (counter != count) {
if (count - counter <= 1000) setCounter(counter + 1);
else if (counter < 10000) setCounter(counter + 1000);
else setCounter(counter + 10000);
}
}, [counter]);

function numberWithCommas(x: number) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

return <>{numberWithCommas(counter)}</>;
}
11 changes: 11 additions & 0 deletions src/Country.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface Countries {
[country: string]: {
[date: string]: Country;
};
}

export interface Country {
confirmed: number;
recoveries: number;
deaths: number;
}
8 changes: 8 additions & 0 deletions src/GlobalCounts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class GlobalCounts {
static totalConfirmed: number = 0;
static totalDeaths: number = 0;
static totalRecoveries: number = 0;
static totalActive: number = 0;
static set: boolean;
private constructor() {}
}
Loading

0 comments on commit fc46197

Please sign in to comment.