From e0b99a6bc0b5888b084db60448db355007e1ccd9 Mon Sep 17 00:00:00 2001 From: nina992 <89770889+nina992@users.noreply.github.com> Date: Fri, 2 Dec 2022 16:38:41 +0300 Subject: [PATCH] feat: support Cesium Ion terrain (#331) * custom-terrain-url * update * Bug fix * Revert "Bug fix" This reverts commit 7b773b4c4561844b1ace1ca26a240bc367ff36f0. * fix: remove get-pixels dependency and used CanvasAPI for PNG conversion to multidimensional array * remove comments and logging statements * update * fix * remove get-pixels * relocated the formatting helper statements * fix * remove gsi terrain * refactor Co-authored-by: nina992 Co-authored-by: rot1024 Co-authored-by: Piyush Chauhan --- .../Visualizer/Engine/Cesium/core/Globe.tsx | 113 ++++++++++++++++++ .../Visualizer/Engine/Cesium/hooks.ts | 42 +------ .../Visualizer/Engine/Cesium/index.tsx | 16 +-- .../Visualizer/Engine/Cesium/terrain.ts | 20 ---- .../molecules/Visualizer/Engine/ref.ts | 27 +++-- src/util/util.ts | 9 ++ yarn.lock | 6 +- 7 files changed, 142 insertions(+), 91 deletions(-) create mode 100644 src/components/molecules/Visualizer/Engine/Cesium/core/Globe.tsx delete mode 100644 src/components/molecules/Visualizer/Engine/Cesium/terrain.ts diff --git a/src/components/molecules/Visualizer/Engine/Cesium/core/Globe.tsx b/src/components/molecules/Visualizer/Engine/Cesium/core/Globe.tsx new file mode 100644 index 000000000..fa97066d0 --- /dev/null +++ b/src/components/molecules/Visualizer/Engine/Cesium/core/Globe.tsx @@ -0,0 +1,113 @@ +import { + ArcGISTiledElevationTerrainProvider, + CesiumTerrainProvider, + EllipsoidTerrainProvider, + IonResource, + TerrainProvider, +} from "cesium"; +import { pick } from "lodash-es"; +import { useMemo } from "react"; +import { Globe as CesiumGlobe } from "resium"; + +import { objKeys } from "@reearth/util/util"; + +import type { SceneProperty, TerrainProperty } from "../../ref"; + +export type Props = { + property?: SceneProperty; + cesiumIonAccessToken?: string; +}; + +export default function Globe({ property, cesiumIonAccessToken }: Props): JSX.Element | null { + const terrainProperty = useMemo( + (): TerrainProperty => ({ + ...property?.terrain, + ...pick(property?.default, terrainPropertyKeys), + }), + [property?.terrain, property?.default], + ); + + const terrainProvider = useMemo((): TerrainProvider | undefined => { + const opts = { + terrain: terrainProperty?.terrain, + terrainType: terrainProperty?.terrainType, + terrainCesiumIonAccessToken: + terrainProperty?.terrainCesiumIonAccessToken || cesiumIonAccessToken, + terrainCesiumIonAsset: terrainProperty?.terrainCesiumIonAsset, + terrainCesiumIonUrl: terrainProperty?.terrainCesiumIonUrl, + }; + const provider = opts.terrain ? terrainProviders[opts.terrainType || "cesium"] : undefined; + return (typeof provider === "function" ? provider(opts) : provider) ?? defaultTerrainProvider; + }, [ + terrainProperty?.terrain, + terrainProperty?.terrainType, + terrainProperty?.terrainCesiumIonAccessToken, + terrainProperty?.terrainCesiumIonAsset, + terrainProperty?.terrainCesiumIonUrl, + cesiumIonAccessToken, + ]); + + return ( + + ); +} + +const terrainPropertyKeys = objKeys({ + terrain: 0, + terrainType: 0, + terrainExaggeration: 0, + terrainExaggerationRelativeHeight: 0, + depthTestAgainstTerrain: 0, + terrainCesiumIonAsset: 0, + terrainCesiumIonAccessToken: 0, + terrainCesiumIonUrl: 0, + terrainUrl: 0, +}); + +const defaultTerrainProvider = new EllipsoidTerrainProvider(); + +const terrainProviders: { + [k in NonNullable]: + | TerrainProvider + | (( + opts: Pick< + TerrainProperty, + "terrainCesiumIonAccessToken" | "terrainCesiumIonAsset" | "terrainCesiumIonUrl" + >, + ) => TerrainProvider | null); +} = { + cesium: ({ terrainCesiumIonAccessToken }) => + // https://github.com/CesiumGS/cesium/blob/main/Source/Core/createWorldTerrain.js + new CesiumTerrainProvider({ + url: IonResource.fromAssetId(1, { + accessToken: terrainCesiumIonAccessToken, + }), + requestVertexNormals: false, + requestWaterMask: false, + }), + arcgis: () => + new ArcGISTiledElevationTerrainProvider({ + url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer", + }), + cesiumion: ({ terrainCesiumIonAccessToken, terrainCesiumIonAsset, terrainCesiumIonUrl }) => + terrainCesiumIonAsset + ? new CesiumTerrainProvider({ + url: + terrainCesiumIonUrl || + IonResource.fromAssetId(parseInt(terrainCesiumIonAsset, 10), { + accessToken: terrainCesiumIonAccessToken, + }), + requestVertexNormals: true, + }) + : null, +}; diff --git a/src/components/molecules/Visualizer/Engine/Cesium/hooks.ts b/src/components/molecules/Visualizer/Engine/Cesium/hooks.ts index da8b27794..05f53fb9a 100644 --- a/src/components/molecules/Visualizer/Engine/Cesium/hooks.ts +++ b/src/components/molecules/Visualizer/Engine/Cesium/hooks.ts @@ -1,5 +1,5 @@ import { Color, Entity, Cesium3DTileFeature, Cartesian3, Clock as CesiumClock, Ion } from "cesium"; -import type { Viewer as CesiumViewer, TerrainProvider } from "cesium"; +import type { Viewer as CesiumViewer } from "cesium"; import CesiumDnD, { Context } from "cesium-dnd"; import { isEqual } from "lodash-es"; import { useCallback, useEffect, useMemo, useRef } from "react"; @@ -22,7 +22,6 @@ import { getLocationFromScreen, getClock, } from "./common"; -import terrain from "./terrain"; import useEngineRef from "./useEngineRef"; import { convertCartesian3ToPosition } from "./utils"; @@ -63,43 +62,6 @@ export default ({ // expose ref const engineAPI = useEngineRef(ref, cesium); - // terrain - const terrainProperty = useMemo( - () => ({ - terrain: property?.terrain?.terrain || property?.default?.terrain, - terrainType: property?.terrain?.terrainType || property?.default?.terrainType, - terrainExaggeration: - property?.terrain?.terrainExaggeration || property?.default?.terrainExaggeration, - terrainExaggerationRelativeHeight: - property?.terrain?.terrainExaggerationRelativeHeight || - property?.default?.terrainExaggerationRelativeHeight, - depthTestAgainstTerrain: - property?.terrain?.depthTestAgainstTerrain || property?.default?.depthTestAgainstTerrain, - }), - [ - property?.default?.terrain, - property?.default?.terrainType, - property?.default?.terrainExaggeration, - property?.default?.terrainExaggerationRelativeHeight, - property?.default?.depthTestAgainstTerrain, - property?.terrain?.terrain, - property?.terrain?.terrainType, - property?.terrain?.terrainExaggeration, - property?.terrain?.terrainExaggerationRelativeHeight, - property?.terrain?.depthTestAgainstTerrain, - ], - ); - - const terrainProvider = useMemo((): TerrainProvider | undefined => { - const provider = terrainProperty.terrain - ? terrainProperty.terrainType - ? terrain[terrainProperty.terrainType] || terrain.default - : terrain.cesium - : terrain.default; - return typeof provider === "function" ? provider({ cesiumIonAccessToken }) : provider; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [cesiumIonAccessToken, terrainProperty.terrain, terrainProperty.terrainType]); - const backgroundColor = useMemo( () => property?.default?.bgcolor ? Color.fromCssColorString(property.default.bgcolor) : undefined, @@ -358,8 +320,6 @@ export default ({ useCameraLimiter(cesium, camera, property?.cameraLimiter); return { - terrainProvider, - terrainProperty, backgroundColor, cesium, cameraViewBoundaries, diff --git a/src/components/molecules/Visualizer/Engine/Cesium/index.tsx b/src/components/molecules/Visualizer/Engine/Cesium/index.tsx index 2c005ef27..1249e9725 100644 --- a/src/components/molecules/Visualizer/Engine/Cesium/index.tsx +++ b/src/components/molecules/Visualizer/Engine/Cesium/index.tsx @@ -2,7 +2,6 @@ import { ArcType, Color, ScreenSpaceEventType } from "cesium"; import React, { forwardRef } from "react"; import { Viewer, - Globe, Fog, Sun, SkyAtmosphere, @@ -19,6 +18,7 @@ import { import type { EngineProps, Ref as EngineRef } from ".."; import Clock from "./core/Clock"; +import Globe from "./core/Globe"; import ImageryLayers from "./core/Imagery"; import Indicator from "./core/Indicator"; import Event from "./Event"; @@ -50,8 +50,6 @@ const Cesium: React.ForwardRefRenderFunction = ( ref, ) => { const { - terrainProvider, - terrainProperty, backgroundColor, cesium, cameraViewBoundaries, @@ -175,17 +173,7 @@ const Cesium: React.ForwardRefRenderFunction = ( /> - + {ready ? children : null} ); diff --git a/src/components/molecules/Visualizer/Engine/Cesium/terrain.ts b/src/components/molecules/Visualizer/Engine/Cesium/terrain.ts deleted file mode 100644 index 799bbde37..000000000 --- a/src/components/molecules/Visualizer/Engine/Cesium/terrain.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - ArcGISTiledElevationTerrainProvider, - EllipsoidTerrainProvider, - IonResource, - CesiumTerrainProvider, -} from "cesium"; - -export default { - default: new EllipsoidTerrainProvider(), - cesium: ({ cesiumIonAccessToken }: { cesiumIonAccessToken?: string }) => - // https://github.com/CesiumGS/cesium/blob/main/Source/Core/createWorldTerrain.js - new CesiumTerrainProvider({ - url: IonResource.fromAssetId(1, { accessToken: cesiumIonAccessToken }), - requestVertexNormals: false, - requestWaterMask: false, - }), - arcgis: new ArcGISTiledElevationTerrainProvider({ - url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer", - }), -}; diff --git a/src/components/molecules/Visualizer/Engine/ref.ts b/src/components/molecules/Visualizer/Engine/ref.ts index 0433d5b78..4ee96db8a 100644 --- a/src/components/molecules/Visualizer/Engine/ref.ts +++ b/src/components/molecules/Visualizer/Engine/ref.ts @@ -134,20 +134,27 @@ export type ClusterProps = { children?: ReactNode; }; +export type TerrainProperty = { + terrain?: boolean; + terrainType?: "cesium" | "arcgis" | "cesiumion"; // default: cesium + terrainExaggeration?: number; // default: 1 + terrainExaggerationRelativeHeight?: number; // default: 0 + depthTestAgainstTerrain?: boolean; + terrainCesiumIonAsset?: string; + terrainCesiumIonAccessToken?: string; + terrainCesiumIonUrl?: string; + terrainUrl?: string; +}; + export type SceneProperty = { default?: { camera?: Camera; - terrain?: boolean; - terrainType?: "cesium" | "arcgis"; // default: cesium - terrainExaggeration?: number; // default: 1 - terrainExaggerationRelativeHeight?: number; // default: 0 - depthTestAgainstTerrain?: boolean; allowEnterGround?: boolean; skybox?: boolean; bgcolor?: string; ion?: string; sceneMode?: SceneMode; // default: scene3d - }; + } & TerrainProperty; cameraLimiter?: { cameraLimitterEnabled?: boolean; cameraLimitterShowHelper?: boolean; @@ -168,13 +175,7 @@ export type SceneProperty = { tile_minLevel?: number; tile_opacity?: number; }[]; - terrain?: { - terrain?: boolean; - terrainType?: "cesium" | "arcgis"; // default: cesium - terrainExaggeration?: number; // default: 1 - terrainExaggerationRelativeHeight?: number; // default: 0 - depthTestAgainstTerrain?: boolean; - }; + terrain?: TerrainProperty; atmosphere?: { enable_sun?: boolean; enable_lighting?: boolean; diff --git a/src/util/util.ts b/src/util/util.ts index c52747631..7c16bfae1 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -1,5 +1,14 @@ import { Args, Args3, Args2 } from "@reearth/types"; +/** + * Often we want to make an array of keys of an object type, + * but if we just specify the key names directly, we may forget to change the array if the object type is changed. + * With this function, the compiler checks the object keys for completeness, so the array of keys is always up to date. + */ +export const objKeys = (obj: { [k in keyof T]: 0 }): (keyof T)[] => { + return Object.keys(obj) as (keyof T)[]; +}; + export const isPresent = (v: V | undefined): v is V => typeof v !== "undefined"; export const partitionObject = ( diff --git a/yarn.lock b/yarn.lock index 1ac1d2337..412bca149 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2262,9 +2262,9 @@ "@types/gapi.client.discovery" "*" "@maxim_mazurok/gapi.client.sheets-v4@latest": - version "0.0.20221031" - resolved "https://registry.yarnpkg.com/@maxim_mazurok/gapi.client.sheets-v4/-/gapi.client.sheets-v4-0.0.20221031.tgz#f586b972a7897afadf6f8b49c0336f0c944a6a1f" - integrity sha512-BxEMZ7l0C7csdKLmSSRwk9SyM8PaSQirrgEYTya34pWRKAfYgdrUpA2NnN4iSXbdWluohuBcBcZj920FSqy0WQ== + version "0.0.20221115" + resolved "https://registry.yarnpkg.com/@maxim_mazurok/gapi.client.sheets-v4/-/gapi.client.sheets-v4-0.0.20221115.tgz#5bd60c2dfb780167dbe5d0ee690f7139e1854aa9" + integrity sha512-xMLfeiYJx3HWBybGDSlU/RHShY/0oXaoI5qLmchOJBRslEYbLFF2o7TJgR5JWTuG7mj3n+QtjZhV7FGoyU5/og== dependencies: "@types/gapi.client" "*" "@types/gapi.client.discovery" "*"