Skip to content

Commit

Permalink
Env var added + Added new example and clean up function for React str…
Browse files Browse the repository at this point in the history
…ict dev mode
  • Loading branch information
hyundotio committed May 19, 2024
1 parent 5132d50 commit ea09461
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 12 deletions.
85 changes: 73 additions & 12 deletions Components/CesiumComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React from 'react'
import type { CesiumType } from '../types/cesium'
import type { Entity, Viewer } from 'cesium';
import type { Cesium3DTileset, Entity, Viewer } from 'cesium';
import type { Position } from '../types/position';
//NOTE: It is important to assign types using "import type", not "import"
import { dateToJulianDate } from '../example_utils/date';
Expand All @@ -18,23 +18,55 @@ export const CesiumComponent: React.FunctionComponent<{
}) => {
const cesiumViewer = React.useRef<Viewer | null>(null);
const cesiumContainerRef = React.useRef<HTMLDivElement>(null);
const addedScenePrimitives = React.useRef<Cesium3DTileset[]>([]);
const [isLoaded, setIsLoaded] = React.useState(false);

React.useEffect(() => {
if (cesiumViewer.current === null && cesiumContainerRef.current) {
//NOTE: Always utilize CesiumJs; do not import them from "cesium"
cesiumViewer.current = new CesiumJs.Viewer(cesiumContainerRef.current);
//NOTE: Example of configuring a Cesium viewer
cesiumViewer.current.clock.clockStep = CesiumJs.ClockStep.SYSTEM_CLOCK_MULTIPLIER;
const resetCamera = React.useCallback(async () => {
// Set the initial camera to look at Seattle
// No need for dependancies since all data is static for this example.
if (cesiumViewer.current !== null) {
cesiumViewer.current.scene.camera.setView({
destination: CesiumJs.Cartesian3.fromDegrees(-122.3472, 47.598, 370),
orientation: {
heading: CesiumJs.Math.toRadians(10),
pitch: CesiumJs.Math.toRadians(-10),
},
});
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

React.useEffect(() => {
if (isLoaded) return;
const cleanUpPrimitives = React.useCallback(() => {
//On NextJS 13.4+, React Strict Mode is on by default.
//The block below will remove all added primitives from the scene.
addedScenePrimitives.current.forEach(scenePrimitive => {
if (cesiumViewer.current !== null) {
cesiumViewer.current.scene.primitives.remove(scenePrimitive);
}
});
addedScenePrimitives.current = [];
}, []);

const initializeCesiumJs = React.useCallback(async () => {
if (cesiumViewer.current !== null) {
//Using the Sandcastle example below
//https://sandcastle.cesium.com/?src=3D%20Tiles%20Feature%20Styling.html
const osmBuildingsTileset = await CesiumJs.createOsmBuildingsAsync();

//Clean up potentially already-existing primitives.
cleanUpPrimitives();

//Adding tile and adding to addedScenePrimitives to keep track and delete in-case of a re-render.
const osmBuildingsTilesetPrimitive = cesiumViewer.current.scene.primitives.add(osmBuildingsTileset);
addedScenePrimitives.current.push(osmBuildingsTilesetPrimitive);

//Position camera per Sandcastle demo
resetCamera();

//We'll also add our own data here (In Philadelphia) passed down from props as an example
positions.forEach(p => {
const newEntity = cesiumViewer.current?.entities.add({
cesiumViewer.current?.entities.add({
position: CesiumJs.Cartesian3.fromDegrees(p.lng, p.lat),
ellipse: {
semiMinorAxis: 50000.0,
Expand All @@ -45,12 +77,41 @@ export const CesiumComponent: React.FunctionComponent<{
outlineColor: CesiumJs.Color.BLACK,
}
});
})
});

//Set loaded flag
setIsLoaded(true);

// eslint-disable-next-line react-hooks/exhaustive-deps
}
}, [positions]);

React.useEffect(() => {
if (cesiumViewer.current === null && cesiumContainerRef.current) {
//OPTIONAL: Assign access Token here
//Guide: https://cesium.com/learn/ion/cesium-ion-access-tokens/
CesiumJs.Ion.defaultAccessToken = `${process.env.NEXT_PUBLIC_CESIUM_TOKEN}`;

//NOTE: Always utilize CesiumJs; do not import them from "cesium"
cesiumViewer.current = new CesiumJs.Viewer(cesiumContainerRef.current, {
//Using the Sandcastle example below
//https://sandcastle.cesium.com/?src=3D%20Tiles%20Feature%20Styling.html
terrain: CesiumJs.Terrain.fromWorldTerrain()
});

//NOTE: Example of configuring a Cesium viewer
cesiumViewer.current.clock.clockStep = CesiumJs.ClockStep.SYSTEM_CLOCK_MULTIPLIER;
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

React.useEffect(() => {
if (isLoaded) return;
initializeCesiumJs();

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [positions, isLoaded]);


//NOTE: Examples of typing... See above on "import type"
const entities: Entity[] = [];
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ https://github.com/hyundotio/nextjs-ts-cesium-example <- Click here for App rout
#### Demo
Literally just pointed Vercel to this repo to build and run automatically.

#### Environment Variable
In your `.env` file, add a field called `NEXT_PUBLIC_CESIUM_TOKEN` and assign your Cesium Access Token. This is optional if you plan on using other services.

#### next.config.js
Cesium requires some files to be copied in a publicly accessible folder. This is achieved with CopyWebPackPlugin... *BUT* each copy statements requires `info: { minimized: true }`

Expand All @@ -20,6 +23,9 @@ It is just cleaner to have Cesium related components as client only components.

I won't go into every method that I've tried but every method I've tried has resulted in browser errors, Next.js errors, and/or Vercel (500 status filesystem) errors. Using the CopyWebPackPlugin, wrapping it in a dynamic component, and then finally importing the Cesium files via `import` inside a `useEffect` yielded 100% success.

#### Next.js + React quirks
On NextJS 13.4+ React Strict-Mode is enabled by default. In `CesiumComponent.tsx` there is some extra code in `initializeCesiumJs` function and the `cleanUpPrimitives` function itself to clean up any possible things (in this specific case, primitives) that may be added as a duplicate when the function is called twice. Optionally, you can disable React strcit mode and remove this code.

#### TypeScript shenanigans with Cesium.js
With all the work above, it is very important to utilize the dynamically called Cesium and not import individual functions like you would normally. Also it is very important to type Cesium specific things with `import type { xyz } from 'cesium'` not `import { xyz } from 'cesium'`

Expand Down

0 comments on commit ea09461

Please sign in to comment.