@reearth/core 0.0.7-alpha.43 → 0.0.7-alpha.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reearth/core",
3
- "version": "0.0.7-alpha.43",
3
+ "version": "0.0.7-alpha.45",
4
4
  "author": "Re:Earth contributors <community@reearth.io>",
5
5
  "license": "Apache-2.0",
6
6
  "description": "A library that abstracts a map engine as one common API.",
@@ -82,7 +82,7 @@ export default function useHooks(
82
82
  info: SelectedFeatureInfo | undefined,
83
83
  ) => {
84
84
  const isSketchLayer =
85
- selectedLayer.layer?.layer.type === "simple" &&
85
+ selectedLayer.layer?.layer?.type === "simple" &&
86
86
  selectedLayer.layer?.layer?.data?.isSketchLayer;
87
87
  // Sketch layer feature has a fixed featureId, we need to exclude it from the skip condition
88
88
  if (
@@ -177,7 +177,7 @@ const convertStyle = (val: any, convert: StyleProperty["convert"]) => {
177
177
 
178
178
  const useFeature = ({
179
179
  id,
180
- tileset,
180
+ tilesetRef,
181
181
  idProperty,
182
182
  layer,
183
183
  viewer,
@@ -189,7 +189,7 @@ const useFeature = ({
189
189
  isTilesetReady,
190
190
  }: {
191
191
  id?: string;
192
- tileset: MutableRefObject<Cesium3DTileset | undefined>;
192
+ tilesetRef: MutableRefObject<Cesium3DTileset | undefined>;
193
193
  idProperty?: string;
194
194
  layer?: ComputedLayer;
195
195
  viewer?: Viewer;
@@ -327,10 +327,10 @@ const useFeature = ({
327
327
  handleTilesetLoadRef.current = handleTilesetLoad;
328
328
  useEffect(
329
329
  () =>
330
- tileset.current?.tileLoad.addEventListener((t: Cesium3DTile) =>
330
+ tilesetRef.current?.tileLoad.addEventListener((t: Cesium3DTile) =>
331
331
  handleTilesetLoadRef.current(t),
332
332
  ),
333
- [tileset, isTilesetReady],
333
+ [tilesetRef, isTilesetReady],
334
334
  );
335
335
 
336
336
  const handleTilesetUnload = useCallback(
@@ -346,10 +346,10 @@ const useFeature = ({
346
346
  handleTilesetUnloadRef.current = handleTilesetUnload;
347
347
  useEffect(
348
348
  () =>
349
- tileset.current?.tileUnload.addEventListener((t: Cesium3DTile) =>
349
+ tilesetRef.current?.tileUnload.addEventListener((t: Cesium3DTile) =>
350
350
  handleTilesetUnloadRef.current(t),
351
351
  ),
352
- [tileset, isTilesetReady],
352
+ [tilesetRef, isTilesetReady],
353
353
  );
354
354
 
355
355
  useEffect(() => {
@@ -487,6 +487,8 @@ export const useHooks = ({
487
487
  const shouldUseFeatureIndex = !disableIndexingFeature && !!idProperty;
488
488
 
489
489
  const [isTilesetReady, setIsTilesetReady] = useState(false);
490
+ const [isTilesetCompReady, setIsTilesetCompReady] = useState(false);
491
+ const [isTilesetRefReady, setIsTilesetRefReady] = useState(false);
490
492
 
491
493
  const prevPlanes = useRef(_planes);
492
494
  const planes = useMemo(() => {
@@ -566,6 +568,7 @@ export const useHooks = ({
566
568
  (tileset?.cesiumElement as any)[layerIdField] = layer.id;
567
569
  }
568
570
  tilesetRef.current = tileset?.cesiumElement;
571
+ setIsTilesetRefReady(!!tileset?.cesiumElement);
569
572
  },
570
573
  [id, layer?.id, featureIndex, shouldUseFeatureIndex],
571
574
  );
@@ -602,7 +605,7 @@ export const useHooks = ({
602
605
 
603
606
  useFeature({
604
607
  id,
605
- tileset: tilesetRef,
608
+ tilesetRef,
606
609
  layer,
607
610
  idProperty,
608
611
  viewer,
@@ -806,13 +809,18 @@ export const useHooks = ({
806
809
 
807
810
  const handleReady = useCallback(
808
811
  (tileset: Cesium3DTileset) => {
809
- setIsTilesetReady(true);
812
+ setIsTilesetCompReady(true);
810
813
  onLayerFetch?.({ properties: tileset.properties });
811
814
  onLayerLoad?.({ layerId: layerIdRef.current });
812
815
  },
813
816
  [onLayerFetch, onLayerLoad],
814
817
  );
815
818
 
819
+ useEffect(() => {
820
+ if (!isTilesetCompReady || !isTilesetRefReady) return;
821
+ setIsTilesetReady(true);
822
+ }, [isTilesetCompReady, isTilesetRefReady]);
823
+
816
824
  useEffect(() => {
817
825
  updateCredits?.();
818
826
  return () => {
@@ -0,0 +1,73 @@
1
+ import { useEffect, useMemo, useRef } from "react";
2
+ import { Globe as CesiumGlobe } from "resium";
3
+
4
+ import type { ViewerProperty } from "../../..";
5
+ import { toColor } from "../../common";
6
+
7
+ import useTerrainProviderPromise from "./useTerrainProviderPromise";
8
+
9
+ export type Props = {
10
+ property?: ViewerProperty;
11
+ cesiumIonAccessToken?: string;
12
+ onTerrainProviderChange?: () => void;
13
+ };
14
+
15
+ export default function Globe({
16
+ property,
17
+ cesiumIonAccessToken,
18
+ onTerrainProviderChange,
19
+ }: Props): JSX.Element | null {
20
+ const providerPromise = useTerrainProviderPromise({
21
+ terrain: property?.terrain?.enabled,
22
+ terrainType: property?.terrain?.type,
23
+ normal: property?.terrain?.normal,
24
+ ionAccessToken: property?.assets?.cesium?.terrain?.ionAccessToken || cesiumIonAccessToken,
25
+ ionAsset: property?.assets?.cesium?.terrain?.ionAsset,
26
+ ionUrl: property?.assets?.cesium?.terrain?.ionUrl,
27
+ });
28
+
29
+ const baseColor = useMemo(
30
+ () => toColor(property?.globe?.baseColor),
31
+ [property?.globe?.baseColor],
32
+ );
33
+
34
+ const lastResolvedProviderRef = useRef<any>(null);
35
+
36
+ useEffect(() => {
37
+ let isCancelled = false;
38
+
39
+ providerPromise
40
+ .then(resolvedProvider => {
41
+ if (isCancelled) return;
42
+
43
+ // Only trigger callback if the resolved provider is actually different
44
+ if (lastResolvedProviderRef.current !== resolvedProvider) {
45
+ lastResolvedProviderRef.current = resolvedProvider;
46
+ onTerrainProviderChange?.();
47
+ }
48
+ })
49
+ .catch(error => {
50
+ if (!isCancelled) {
51
+ console.warn("Terrain provider failed to load:", error);
52
+ }
53
+ });
54
+
55
+ return () => {
56
+ isCancelled = true;
57
+ };
58
+ }, [providerPromise, onTerrainProviderChange]);
59
+
60
+ return (
61
+ <CesiumGlobe
62
+ baseColor={baseColor}
63
+ enableLighting={!!property?.globe?.enableLighting}
64
+ showGroundAtmosphere={property?.globe?.atmosphere?.enabled ?? true}
65
+ atmosphereLightIntensity={property?.globe?.atmosphere?.lightIntensity}
66
+ atmosphereSaturationShift={property?.globe?.atmosphere?.saturationShift}
67
+ atmosphereHueShift={property?.globe?.atmosphere?.hueShift}
68
+ atmosphereBrightnessShift={property?.globe?.atmosphere?.brightnessShift}
69
+ terrainProvider={providerPromise}
70
+ depthTestAgainstTerrain={!!property?.globe?.depthTestAgainstTerrain}
71
+ />
72
+ );
73
+ }
@@ -0,0 +1,79 @@
1
+ import {
2
+ ArcGISTiledElevationTerrainProvider,
3
+ CesiumTerrainProvider,
4
+ EllipsoidTerrainProvider,
5
+ IonResource,
6
+ TerrainProvider,
7
+ } from "cesium";
8
+ import { useMemo, useRef } from "react";
9
+
10
+ import { TerrainProperty } from "../../..";
11
+ import { AssetsCesiumProperty } from "../../../../Map";
12
+
13
+ type TerrainType = NonNullable<TerrainProperty["type"]>;
14
+
15
+ type ProviderOpts = Pick<TerrainProperty, "normal"> &
16
+ AssetsCesiumProperty["terrain"] & {
17
+ terrain?: boolean;
18
+ terrainType?: TerrainType | null | undefined;
19
+ ionAccessToken?: string | undefined;
20
+ };
21
+
22
+ export default function useTerrainProviderPromise(opts: ProviderOpts) {
23
+ // Cache promises so we don’t recreate providers on every toggle
24
+ const cacheRef = useRef(new Map<string, Promise<TerrainProvider>>());
25
+ const ellipsoidRef = useRef<TerrainProvider>();
26
+
27
+ return useMemo<Promise<TerrainProvider>>(() => {
28
+ if (!opts.terrain) {
29
+ // single shared ellipsoid provider
30
+ if (!ellipsoidRef.current) ellipsoidRef.current = new EllipsoidTerrainProvider();
31
+ return Promise.resolve(ellipsoidRef.current);
32
+ }
33
+
34
+ const kind = (opts.terrainType ?? "cesium") as TerrainType;
35
+ const key = makeKey(kind, opts);
36
+ let p = cacheRef.current.get(key);
37
+ if (!p) {
38
+ p = createProvider(kind, opts);
39
+ cacheRef.current.set(key, p);
40
+ }
41
+ return p;
42
+ }, [opts]);
43
+ }
44
+
45
+ function makeKey(type: TerrainType, opts: ProviderOpts) {
46
+ // Key should change when output provider would differ
47
+ const asset = opts.ionAsset ?? "";
48
+ const url = opts.ionUrl ?? "";
49
+ const normal = String(!!opts.normal);
50
+ return `${type}|asset:${asset}|url:${url}|normal:${normal}`;
51
+ }
52
+
53
+ function createProvider(type: TerrainType, opts: ProviderOpts): Promise<TerrainProvider> {
54
+ switch (type) {
55
+ case "cesium":
56
+ return CesiumTerrainProvider.fromUrl(
57
+ IonResource.fromAssetId(1, { accessToken: opts.ionAccessToken }),
58
+ { requestVertexNormals: !!opts.normal, requestWaterMask: false },
59
+ ) as Promise<TerrainProvider>;
60
+
61
+ case "arcgis":
62
+ return ArcGISTiledElevationTerrainProvider.fromUrl(
63
+ "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
64
+ ) as Promise<TerrainProvider>;
65
+
66
+ case "cesiumion":
67
+ if (!opts.ionAsset && !opts.ionUrl) {
68
+ // Fallback to ellipsoid when misconfigured
69
+ return Promise.resolve(new EllipsoidTerrainProvider());
70
+ }
71
+ return CesiumTerrainProvider.fromUrl(
72
+ opts.ionUrl ??
73
+ IonResource.fromAssetId(parseInt(String(opts.ionAsset), 10), {
74
+ accessToken: opts.ionAccessToken,
75
+ }),
76
+ { requestVertexNormals: !!opts.normal },
77
+ ) as Promise<TerrainProvider>;
78
+ }
79
+ }
@@ -63,6 +63,12 @@ function makeGlobeShadersDirty(globe: Globe): void {
63
63
  // reset surface shader source to the initial state (assuming that we never
64
64
  // use custom material on globe).
65
65
  // ref: https://github.com/CesiumGS/cesium/blob/1.106/packages/engine/Source/Scene/Globe.js#L562-L572
66
+
67
+ // Safety check: ensure globe's internal properties exist before manipulation
68
+ if (!globe || globe.isDestroyed() || !(globe as any)._surface) {
69
+ return;
70
+ }
71
+
66
72
  const material = globe.material;
67
73
  if (material == null) {
68
74
  globe.material = Material.fromType("Color");
@@ -73,6 +79,18 @@ function makeGlobeShadersDirty(globe: Globe): void {
73
79
  }
74
80
  }
75
81
 
82
+ function withUniforms(globe: PrivateCesiumGlobe, add?: Record<string, () => any>) {
83
+ if (!globe._surface?._tileProvider) return;
84
+ const mm = (globe._surface._tileProvider as any)?.materialUniformMap ?? {};
85
+ (globe._surface._tileProvider as any).materialUniformMap = { ...mm, ...(add ?? {}) };
86
+ }
87
+
88
+ function removeUniforms(globe: PrivateCesiumGlobe, keys: string[]) {
89
+ const tp = globe._surface?._tileProvider as any;
90
+ if (!tp?.materialUniformMap) return;
91
+ for (const k of keys) delete tp.materialUniformMap[k];
92
+ }
93
+
76
94
  const useIBL = ({
77
95
  sphericalHarmonicCoefficients,
78
96
  globeImageBasedLighting,
@@ -184,6 +202,18 @@ const useTerrainHeatmap = ({
184
202
  return { isCustomHeatmapEnabled, shaderForTerrainHeatmap };
185
203
  };
186
204
 
205
+ async function waitTerrainReady(scene: any) {
206
+ const t = scene.terrain;
207
+ if (t?.ready) return;
208
+ await new Promise<void>(resolve => {
209
+ if (!t) return resolve();
210
+ const off = t.readyEvent.addEventListener(() => {
211
+ off();
212
+ resolve();
213
+ });
214
+ });
215
+ }
216
+
187
217
  export const useOverrideGlobeShader = ({
188
218
  cesium,
189
219
  sphericalHarmonicCoefficients,
@@ -201,6 +231,8 @@ export const useOverrideGlobeShader = ({
201
231
  enableLighting?: boolean;
202
232
  terrain: TerrainProperty | undefined;
203
233
  }) => {
234
+ const applyingRef = useRef(false);
235
+
204
236
  const { uniformMapForIBL, isIBLEnabled, shaderForIBL } = useIBL({
205
237
  sphericalHarmonicCoefficients,
206
238
  globeImageBasedLighting,
@@ -223,75 +255,85 @@ export const useOverrideGlobeShader = ({
223
255
 
224
256
  const needUpdateGlobeRef = useRef(false);
225
257
 
226
- const handleGlobeShader = useCallback(() => {
258
+ const handleGlobeShader = useCallback(async () => {
227
259
  // NOTE: Support the spherical harmonic coefficient only when the terrain normal is enabled.
228
260
  // Because it's difficult to control the shader for the entire globe.
229
261
  // ref: https://github.com/CesiumGS/cesium/blob/af4e2bebbef25259f049b05822adf2958fce11ff/packages/engine/Source/Shaders/GlobeFS.glsl#L408
230
262
  if (!cesium.current?.cesiumElement || !needUpdateGlobeRef.current) return;
231
263
 
232
- const globe = cesium.current.cesiumElement.scene.globe as PrivateCesiumGlobe;
264
+ if (applyingRef.current) return;
265
+ applyingRef.current = true;
233
266
 
234
- const surfaceShaderSet = globe._surfaceShaderSet;
235
- if (!surfaceShaderSet) {
236
- if (import.meta.env.DEV) {
237
- throw new Error("`globe._surfaceShaderSet` could not found");
238
- }
239
- return;
240
- }
267
+ try {
268
+ const { scene } = cesium.current.cesiumElement;
269
+ await waitTerrainReady(scene);
241
270
 
242
- const baseFragmentShaderSource = surfaceShaderSet.baseFragmentShaderSource;
271
+ const globe = cesium.current.cesiumElement.scene.globe as PrivateCesiumGlobe;
243
272
 
244
- const GlobeFS = baseFragmentShaderSource?.sources[baseFragmentShaderSource.sources.length - 1];
273
+ // Reset shaders first so we patch the freshest base
274
+ makeGlobeShadersDirty(globe);
245
275
 
246
- if (!GlobeFS || !baseFragmentShaderSource) {
247
- if (import.meta.env.DEV) {
248
- throw new Error("GlobeFS could not find.");
276
+ const surfaceShaderSet = globe._surfaceShaderSet;
277
+ if (!surfaceShaderSet) {
278
+ if (import.meta.env.DEV) {
279
+ throw new Error("`globe._surfaceShaderSet` could not found");
280
+ }
281
+ return;
249
282
  }
250
- return;
251
- }
252
283
 
253
- const matchers: StringMatcher[] = [];
254
- const shaders: string[] = [];
255
- if (isIBLEnabled && globe.enableLighting && globe.terrainProvider.hasVertexNormals) {
256
- matchers.push(shaderForIBL);
257
- shaders.push(IBLFS);
258
- }
284
+ const baseFragmentShaderSource = surfaceShaderSet.baseFragmentShaderSource;
285
+ const GlobeFS =
286
+ baseFragmentShaderSource?.sources[baseFragmentShaderSource.sources.length - 1];
259
287
 
260
- if (isCustomHeatmapEnabled) {
261
- // This will log the variables needed in the shader below.
262
- // we need the minHeight, maxHeight and logarithmic
263
- matchers.push(shaderForTerrainHeatmap);
264
- shaders.push(HeatmapForTerrainFS);
265
- }
288
+ if (!GlobeFS || !baseFragmentShaderSource) {
289
+ if (import.meta.env.DEV) {
290
+ throw new Error("GlobeFS could not find.");
291
+ }
292
+ return;
293
+ }
266
294
 
267
- // This means there is no overridden shader.
268
- if (!matchers.length) return;
295
+ const matchers: StringMatcher[] = [];
296
+ const shaders: string[] = [];
297
+ const terrainHasNormals = !!(globe.terrainProvider as any)?.hasVertexNormals;
269
298
 
270
- needUpdateGlobeRef.current = false;
299
+ if (isIBLEnabled && globe.enableLighting && terrainHasNormals) {
300
+ matchers.push(shaderForIBL);
301
+ shaders.push(IBLFS);
302
+ }
271
303
 
272
- if (!globe?._surface?._tileProvider) {
273
- if (import.meta.env.DEV) {
274
- throw new Error("`globe._surface._tileProvider.materialUniformMap` could not found");
304
+ if (isCustomHeatmapEnabled && terrainHasNormals) {
305
+ // This will log the variables needed in the shader below.
306
+ // we need the minHeight, maxHeight and logarithmic
307
+ matchers.push(shaderForTerrainHeatmap);
308
+ shaders.push(HeatmapForTerrainFS);
275
309
  }
276
- return;
277
- }
278
310
 
279
- makeGlobeShadersDirty(globe);
311
+ // This means there is no overridden shader.
312
+ if (!matchers.length) return;
280
313
 
281
- const replacedGlobeFS = defaultMatcher.concat(...matchers).execute(GlobeFS);
314
+ needUpdateGlobeRef.current = false;
282
315
 
283
- globe._surface._tileProvider.materialUniformMap = {
284
- ...(globe._surface._tileProvider.materialUniformMap ?? {}),
285
- ...uniformMapForIBL,
286
- };
316
+ if (!globe?._surface?._tileProvider) {
317
+ if (import.meta.env.DEV) {
318
+ throw new Error("`globe._surface._tileProvider.materialUniformMap` could not found");
319
+ }
320
+ return;
321
+ }
287
322
 
288
- surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
289
- sources: [
290
- ...baseFragmentShaderSource.sources.slice(0, -1),
291
- GlobeFSDefinitions + replacedGlobeFS + shaders.join(""),
292
- ],
293
- defines: baseFragmentShaderSource.defines,
294
- });
323
+ const replacedGlobeFS = defaultMatcher.concat(...matchers).execute(GlobeFS);
324
+
325
+ withUniforms(globe, isIBLEnabled ? uniformMapForIBL : undefined);
326
+
327
+ surfaceShaderSet.baseFragmentShaderSource = new ShaderSource({
328
+ sources: [
329
+ ...baseFragmentShaderSource.sources.slice(0, -1),
330
+ GlobeFSDefinitions + replacedGlobeFS + shaders.join(""),
331
+ ],
332
+ defines: baseFragmentShaderSource.defines,
333
+ });
334
+ } finally {
335
+ applyingRef.current = false;
336
+ }
295
337
  }, [
296
338
  cesium,
297
339
  isCustomHeatmapEnabled,
@@ -317,6 +359,10 @@ export const useOverrideGlobeShader = ({
317
359
 
318
360
  return () => {
319
361
  if (!globe.isDestroyed()) {
362
+ removeUniforms(globe, [
363
+ "u_reearth_sphericalHarmonicCoefficients",
364
+ "u_reearth_globeImageBasedLighting",
365
+ ]);
320
366
  // Reset customized shader to default
321
367
  makeGlobeShadersDirty(globe);
322
368
  }
@@ -1,113 +0,0 @@
1
- import {
2
- ArcGISTiledElevationTerrainProvider,
3
- CesiumTerrainProvider,
4
- EllipsoidTerrainProvider,
5
- IonResource,
6
- TerrainProvider,
7
- } from "cesium";
8
- import { useEffect, useMemo } from "react";
9
- import { Globe as CesiumGlobe } from "resium";
10
-
11
- import type { ViewerProperty, TerrainProperty } from "../..";
12
- import { AssetsCesiumProperty } from "../../../Map";
13
- import { toColor } from "../common";
14
-
15
- export type Props = {
16
- property?: ViewerProperty;
17
- cesiumIonAccessToken?: string;
18
- onTerrainProviderChange?: () => void;
19
- };
20
-
21
- export default function Globe({
22
- property,
23
- cesiumIonAccessToken,
24
- onTerrainProviderChange,
25
- }: Props): JSX.Element | null {
26
- const terrainProperty = useMemo(
27
- (): TerrainProperty => ({
28
- ...property?.terrain,
29
- }),
30
- [property?.terrain],
31
- );
32
-
33
- const terrainProvider = useMemo((): Promise<TerrainProvider> | TerrainProvider | undefined => {
34
- const opts = {
35
- terrain: terrainProperty?.enabled,
36
- terrainType: terrainProperty?.type,
37
- normal: terrainProperty?.normal,
38
- ionAccessToken: property?.assets?.cesium?.terrain?.ionAccessToken || cesiumIonAccessToken,
39
- ionAsset: property?.assets?.cesium?.terrain?.ionAsset,
40
- ionUrl: property?.assets?.cesium?.terrain?.ionUrl,
41
- };
42
- const provider = opts.terrain ? terrainProviders[opts.terrainType || "cesium"] : undefined;
43
- return (typeof provider === "function" ? provider(opts) : provider) ?? defaultTerrainProvider;
44
- }, [
45
- terrainProperty?.enabled,
46
- terrainProperty?.type,
47
- terrainProperty?.normal,
48
- property?.assets?.cesium?.terrain?.ionAccessToken,
49
- property?.assets?.cesium?.terrain?.ionAsset,
50
- property?.assets?.cesium?.terrain?.ionUrl,
51
- cesiumIonAccessToken,
52
- ]);
53
-
54
- const baseColor = useMemo(
55
- () => toColor(property?.globe?.baseColor),
56
- [property?.globe?.baseColor],
57
- );
58
-
59
- useEffect(() => {
60
- onTerrainProviderChange?.();
61
- }, [terrainProvider, onTerrainProviderChange]);
62
-
63
- return (
64
- <CesiumGlobe
65
- baseColor={baseColor}
66
- enableLighting={!!property?.globe?.enableLighting}
67
- showGroundAtmosphere={property?.globe?.atmosphere?.enabled ?? true}
68
- atmosphereLightIntensity={property?.globe?.atmosphere?.lightIntensity}
69
- atmosphereSaturationShift={property?.globe?.atmosphere?.saturationShift}
70
- atmosphereHueShift={property?.globe?.atmosphere?.hueShift}
71
- atmosphereBrightnessShift={property?.globe?.atmosphere?.brightnessShift}
72
- terrainProvider={terrainProvider}
73
- depthTestAgainstTerrain={!!property?.globe?.depthTestAgainstTerrain}
74
- />
75
- );
76
- }
77
-
78
- const defaultTerrainProvider = new EllipsoidTerrainProvider();
79
-
80
- const terrainProviders: {
81
- [k in NonNullable<TerrainProperty["type"]>]:
82
- | TerrainProvider
83
- | ((
84
- opts: Pick<TerrainProperty, "normal"> & AssetsCesiumProperty["terrain"],
85
- ) => Promise<TerrainProvider> | TerrainProvider | null);
86
- } = {
87
- cesium: ({ ionAccessToken, normal }) =>
88
- CesiumTerrainProvider.fromUrl(
89
- IonResource.fromAssetId(1, {
90
- accessToken: ionAccessToken,
91
- }),
92
- {
93
- requestVertexNormals: normal,
94
- requestWaterMask: false,
95
- },
96
- ),
97
- arcgis: () =>
98
- ArcGISTiledElevationTerrainProvider.fromUrl(
99
- "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
100
- ),
101
- cesiumion: ({ ionAccessToken, ionAsset, ionUrl, normal }) =>
102
- ionAsset
103
- ? CesiumTerrainProvider.fromUrl(
104
- ionUrl ||
105
- IonResource.fromAssetId(parseInt(ionAsset, 10), {
106
- accessToken: ionAccessToken,
107
- }),
108
- {
109
- requestVertexNormals: normal,
110
- },
111
- )
112
- : null,
113
- };