@reearth/core 0.0.7-alpha.71 → 0.0.7-alpha.72

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/dist/index.d.ts CHANGED
@@ -524,6 +524,7 @@ export declare type EngineProps = {
524
524
  meta?: Record<string, unknown>;
525
525
  customProvider?: CustomProviderConfig;
526
526
  displayCredits?: boolean;
527
+ hasCesiumIonAsset?: boolean;
527
528
  layersRef?: RefObject<LayersRef | null>;
528
529
  requestingRenderMode?: MutableRefObject<RequestingRenderMode>;
529
530
  timelineManagerRef?: TimelineManagerRef;
@@ -1146,7 +1147,7 @@ declare const Map_2: ForwardRefExoticComponent< {
1146
1147
  engines?: Record<string, Engine>;
1147
1148
  engine?: string;
1148
1149
  onAPIReady?: () => void;
1149
- } & Omit<Props_2, "Feature" | "viewerProperty" | "selectionReason" | "delegatedDataTypes" | "clusterComponent" | "selectedLayerId"> & Omit<EngineProps, "onLayerSelect" | "selectedLayerId" | "layerSelectionReason"> & Omit<SketchProps, "engineRef" | "SketchComponent" | "layersRef"> & RefAttributes<MapRef>>;
1150
+ } & Omit<Props_2, "Feature" | "viewerProperty" | "selectionReason" | "delegatedDataTypes" | "clusterComponent" | "selectedLayerId"> & Omit<EngineProps, "onLayerSelect" | "selectedLayerId" | "layerSelectionReason" | "hasCesiumIonAsset"> & Omit<SketchProps, "engineRef" | "SketchComponent" | "layersRef"> & RefAttributes<MapRef>>;
1150
1151
  export { Map_2 as Map }
1151
1152
 
1152
1153
  export declare type MapRef = {
@@ -1391,7 +1392,7 @@ export declare type Props = {
1391
1392
  engines?: Record<string, Engine>;
1392
1393
  engine?: string;
1393
1394
  onAPIReady?: () => void;
1394
- } & Omit<Props_2, "Feature" | "clusterComponent" | "selectionReason" | "delegatedDataTypes" | "selectedLayerId" | "viewerProperty"> & Omit<EngineProps, "onLayerSelect" | "layerSelectionReason" | "selectedLayerId"> & Omit<SketchProps, "layersRef" | "engineRef" | "SketchComponent">;
1395
+ } & Omit<Props_2, "Feature" | "clusterComponent" | "selectionReason" | "delegatedDataTypes" | "selectedLayerId" | "viewerProperty"> & Omit<EngineProps, "onLayerSelect" | "layerSelectionReason" | "selectedLayerId" | "hasCesiumIonAsset"> & Omit<SketchProps, "layersRef" | "engineRef" | "SketchComponent">;
1395
1396
 
1396
1397
  declare type Props_2 = Omit<Props_3, "atomMap" | "isHidden" | "selectedLayerId"> & {
1397
1398
  selectedLayer?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reearth/core",
3
- "version": "0.0.7-alpha.71",
3
+ "version": "0.0.7-alpha.72",
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.",
@@ -0,0 +1,245 @@
1
+ import { describe, expect, test } from "vitest";
2
+
3
+ import { computeHasCesiumIonAsset } from "./cesiumIonDetection";
4
+
5
+ const makeSimple = (data?: object) => ({
6
+ id: "layer1",
7
+ type: "simple" as const,
8
+ ...(data ? { data } : {}),
9
+ });
10
+
11
+ const makeGroup = (children: object[]) => ({
12
+ id: "group1",
13
+ type: "group" as const,
14
+ children,
15
+ });
16
+
17
+ describe("computeHasCesiumIonAsset", () => {
18
+ describe("tiles", () => {
19
+ test("returns false when no tiles", () => {
20
+ expect(computeHasCesiumIonAsset({ tiles: [] })).toBe(false);
21
+ });
22
+
23
+ test("returns true for cesium_ion tile type", () => {
24
+ expect(
25
+ computeHasCesiumIonAsset({ tiles: [{ id: "", type: "cesium_ion" }] }),
26
+ ).toBe(true);
27
+ });
28
+
29
+ test("returns true for cesium_ion_default tile type", () => {
30
+ expect(
31
+ computeHasCesiumIonAsset({
32
+ tiles: [{ id: "", type: "cesium_ion_default" }],
33
+ }),
34
+ ).toBe(true);
35
+ });
36
+
37
+ test("returns true for legacy tile types", () => {
38
+ for (const type of [
39
+ "default",
40
+ "default_road",
41
+ "default_label",
42
+ "black_marble",
43
+ ]) {
44
+ expect(computeHasCesiumIonAsset({ tiles: [{ id: "", type }] })).toBe(
45
+ true,
46
+ );
47
+ }
48
+ });
49
+
50
+ test("returns false for non-ion tile type", () => {
51
+ expect(
52
+ computeHasCesiumIonAsset({
53
+ tiles: [{ id: "", type: "open_street_map" }],
54
+ }),
55
+ ).toBe(false);
56
+ });
57
+ });
58
+
59
+ describe("terrain", () => {
60
+ test("returns true for cesium terrain type when enabled", () => {
61
+ expect(
62
+ computeHasCesiumIonAsset({
63
+ terrain: { enabled: true, type: "cesium" },
64
+ }),
65
+ ).toBe(true);
66
+ });
67
+
68
+ test("returns true for cesiumion terrain type when enabled", () => {
69
+ expect(
70
+ computeHasCesiumIonAsset({
71
+ terrain: { enabled: true, type: "cesiumion" },
72
+ }),
73
+ ).toBe(true);
74
+ });
75
+
76
+ test("returns false for cesium terrain when disabled", () => {
77
+ expect(
78
+ computeHasCesiumIonAsset({
79
+ terrain: { enabled: false, type: "cesium" },
80
+ }),
81
+ ).toBe(false);
82
+ });
83
+
84
+ test("returns true for ion terrain URL", () => {
85
+ expect(
86
+ computeHasCesiumIonAsset({
87
+ terrain: { enabled: true, type: "url" },
88
+ assets: {
89
+ cesium: {
90
+ terrain: {
91
+ ionUrl: "https://assets.ion.cesium.com/1/tileset.json",
92
+ },
93
+ },
94
+ },
95
+ } as any),
96
+ ).toBe(true);
97
+ });
98
+
99
+ test("returns false for non-ion terrain URL", () => {
100
+ expect(
101
+ computeHasCesiumIonAsset({
102
+ terrain: { enabled: true, type: "url" },
103
+ assets: {
104
+ cesium: { terrain: { ionUrl: "https://example.com/terrain" } },
105
+ },
106
+ } as any),
107
+ ).toBe(false);
108
+ });
109
+ });
110
+
111
+ describe("layers", () => {
112
+ test("returns true for osm-buildings layer", () => {
113
+ expect(
114
+ computeHasCesiumIonAsset(undefined, [
115
+ makeSimple({ type: "osm-buildings" }),
116
+ ] as any),
117
+ ).toBe(true);
118
+ });
119
+
120
+ test("returns true for google-photorealistic with cesium-ion provider", () => {
121
+ expect(
122
+ computeHasCesiumIonAsset(undefined, [
123
+ makeSimple({ type: "google-photorealistic", provider: "cesium-ion" }),
124
+ ] as any),
125
+ ).toBe(true);
126
+ });
127
+
128
+ test("returns false for google-photorealistic with reearth provider", () => {
129
+ expect(
130
+ computeHasCesiumIonAsset(undefined, [
131
+ makeSimple({ type: "google-photorealistic", provider: "reearth" }),
132
+ ] as any),
133
+ ).toBe(false);
134
+ });
135
+
136
+ test("returns false for google-photorealistic with no provider (google API path)", () => {
137
+ expect(
138
+ computeHasCesiumIonAsset(undefined, [
139
+ makeSimple({ type: "google-photorealistic" }),
140
+ ] as any),
141
+ ).toBe(false);
142
+ });
143
+
144
+ test("returns true for layer with ion URL", () => {
145
+ expect(
146
+ computeHasCesiumIonAsset(undefined, [
147
+ makeSimple({
148
+ type: "3dtiles",
149
+ url: "https://assets.ion.cesium.com/123/tileset.json",
150
+ }),
151
+ ] as any),
152
+ ).toBe(true);
153
+ });
154
+
155
+ test("returns true for any layer type with ion URL", () => {
156
+ expect(
157
+ computeHasCesiumIonAsset(undefined, [
158
+ makeSimple({
159
+ type: "geojson",
160
+ url: "https://assets.ion.cesium.com/456/data.json",
161
+ }),
162
+ ] as any),
163
+ ).toBe(true);
164
+ });
165
+
166
+ test("returns false for layer with non-ion URL", () => {
167
+ expect(
168
+ computeHasCesiumIonAsset(undefined, [
169
+ makeSimple({
170
+ type: "3dtiles",
171
+ url: "https://example.com/tileset.json",
172
+ }),
173
+ ] as any),
174
+ ).toBe(false);
175
+ });
176
+
177
+ test("returns false for layer with no data", () => {
178
+ expect(computeHasCesiumIonAsset(undefined, [makeSimple()] as any)).toBe(
179
+ false,
180
+ );
181
+ });
182
+ });
183
+
184
+ describe("layer groups (recursion)", () => {
185
+ test("returns true when nested layer uses ion", () => {
186
+ const group = makeGroup([makeSimple({ type: "osm-buildings" })]);
187
+ expect(computeHasCesiumIonAsset(undefined, [group] as any)).toBe(true);
188
+ });
189
+
190
+ test("returns false when nested layer does not use ion", () => {
191
+ const group = makeGroup([
192
+ makeSimple({ type: "geojson", url: "https://example.com/data.json" }),
193
+ ]);
194
+ expect(computeHasCesiumIonAsset(undefined, [group] as any)).toBe(false);
195
+ });
196
+
197
+ test("returns true when deeply nested layer uses ion", () => {
198
+ const inner = makeGroup([makeSimple({ type: "osm-buildings" })]);
199
+ const outer = makeGroup([inner]);
200
+ expect(computeHasCesiumIonAsset(undefined, [outer] as any)).toBe(true);
201
+ });
202
+ });
203
+
204
+ describe("combined", () => {
205
+ test("returns false when nothing uses ion", () => {
206
+ expect(
207
+ computeHasCesiumIonAsset(
208
+ {
209
+ tiles: [{ id: "", type: "open_street_map" }],
210
+ terrain: { enabled: false, type: "cesium" },
211
+ },
212
+ [
213
+ makeSimple({
214
+ type: "geojson",
215
+ url: "https://example.com/data.json",
216
+ }),
217
+ ] as any,
218
+ ),
219
+ ).toBe(false);
220
+ });
221
+
222
+ test("returns true when only tile uses ion", () => {
223
+ expect(
224
+ computeHasCesiumIonAsset({ tiles: [{ id: "", type: "default" }] }, [
225
+ makeSimple({ type: "geojson" }),
226
+ ] as any),
227
+ ).toBe(true);
228
+ });
229
+
230
+ test("returns true when only terrain uses ion", () => {
231
+ expect(
232
+ computeHasCesiumIonAsset(
233
+ { terrain: { enabled: true, type: "cesium" } },
234
+ [makeSimple({ type: "geojson" })] as any,
235
+ ),
236
+ ).toBe(true);
237
+ });
238
+
239
+ test("returns undefined-safe (no property, no layers)", () => {
240
+ expect(computeHasCesiumIonAsset()).toBe(false);
241
+ expect(computeHasCesiumIonAsset(undefined, undefined)).toBe(false);
242
+ expect(computeHasCesiumIonAsset(undefined, [])).toBe(false);
243
+ });
244
+ });
245
+ });
@@ -0,0 +1,55 @@
1
+ import type { Layer, LayerSimple } from "../mantle";
2
+
3
+ import type { TileProperty, ViewerProperty } from "./types/viewerProperty";
4
+
5
+ const CESIUM_ION_URL_PATTERN = "ion.cesium.com";
6
+ const CESIUM_ION_LEGACY_TILE_TYPES = new Set([
7
+ "default",
8
+ "default_road",
9
+ "default_label",
10
+ "black_marble",
11
+ ]);
12
+
13
+ function isIonUrl(url?: string | null): boolean {
14
+ return !!url && url.includes(CESIUM_ION_URL_PATTERN);
15
+ }
16
+
17
+ function tileUsesIon(tile: TileProperty): boolean {
18
+ if (!tile.type) return false;
19
+ if (tile.type.startsWith("cesium_ion")) return true;
20
+ if (CESIUM_ION_LEGACY_TILE_TYPES.has(tile.type)) return true;
21
+ return false;
22
+ }
23
+
24
+ function terrainUsesIon(property?: ViewerProperty): boolean {
25
+ const terrain = property?.terrain;
26
+ if (!terrain?.enabled) return false;
27
+ if (terrain.type === "cesium" || terrain.type === "cesiumion") return true;
28
+ if (isIonUrl(property?.assets?.cesium?.terrain?.ionUrl)) return true;
29
+ return false;
30
+ }
31
+
32
+ function layerUsesIon(layer: LayerSimple): boolean {
33
+ const data = layer.data;
34
+ if (!data) return false;
35
+ if (data.type === "osm-buildings") return true;
36
+ if (data.type === "google-photorealistic") {
37
+ return data.provider === "cesium-ion";
38
+ }
39
+ if (isIonUrl(data.url)) return true;
40
+ return false;
41
+ }
42
+
43
+ function anyLayerUsesIon(layer: Layer): boolean {
44
+ if (layer.type === "group") {
45
+ return layer.children.some(anyLayerUsesIon);
46
+ }
47
+ return layerUsesIon(layer);
48
+ }
49
+
50
+ export function computeHasCesiumIonAsset(property?: ViewerProperty, layers?: Layer[]): boolean {
51
+ if (property?.tiles?.some(tileUsesIon)) return true;
52
+ if (terrainUsesIon(property)) return true;
53
+ if (layers?.some(anyLayerUsesIon)) return true;
54
+ return false;
55
+ }
package/src/Map/index.tsx CHANGED
@@ -2,6 +2,7 @@ import { forwardRef, useMemo, type Ref, type JSX } from "react";
2
2
 
3
3
  import { INTERACTION_MODES } from "../shared/interactionMode";
4
4
 
5
+ import { computeHasCesiumIonAsset } from "./cesiumIonDetection";
5
6
  import Geoid from "./Geoid";
6
7
  import useHooks, { MapRef } from "./hooks";
7
8
  import Layers, { type Props as LayersProps } from "./Layers";
@@ -44,7 +45,10 @@ export type Props = {
44
45
  | "selectedLayerId"
45
46
  | "viewerProperty"
46
47
  > &
47
- Omit<EngineProps, "onLayerSelect" | "layerSelectionReason" | "selectedLayerId"> &
48
+ Omit<
49
+ EngineProps,
50
+ "onLayerSelect" | "layerSelectionReason" | "selectedLayerId" | "hasCesiumIonAsset"
51
+ > &
48
52
  Omit<SketchProps, "layersRef" | "engineRef" | "SketchComponent">;
49
53
 
50
54
  function MapFn(
@@ -102,6 +106,11 @@ function MapFn(
102
106
  onAPIReady,
103
107
  });
104
108
 
109
+ const hasCesiumIonAsset = useMemo(
110
+ () => computeHasCesiumIonAsset(props.property, layers),
111
+ [props.property, layers],
112
+ );
113
+
105
114
  const selectedLayerIds = useMemo(
106
115
  () => ({
107
116
  layerId: selectedLayer.layerId,
@@ -125,7 +134,8 @@ function MapFn(
125
134
  onLayerSelect={handleEngineLayerSelect}
126
135
  featureFlags={featureFlags}
127
136
  onMount={handleEngineMount}
128
- {...props}>
137
+ {...props}
138
+ hasCesiumIonAsset={hasCesiumIonAsset}>
129
139
  <Layers
130
140
  ref={layersRef}
131
141
  engineRef={engineRef}
@@ -241,6 +241,7 @@ export type EngineProps = {
241
241
  meta?: Record<string, unknown>;
242
242
  customProvider?: CustomProviderConfig;
243
243
  displayCredits?: boolean;
244
+ hasCesiumIonAsset?: boolean;
244
245
  layersRef?: RefObject<LayersRef | null>;
245
246
  requestingRenderMode?: MutableRefObject<RequestingRenderMode>;
246
247
  timelineManagerRef?: TimelineManagerRef;
@@ -905,7 +905,7 @@ export function getExtrudedHeight(
905
905
  return;
906
906
  }
907
907
 
908
- export function getCredits(viewer: Viewer) {
908
+ export function getCredits(viewer: Viewer, hasCesiumIonAsset?: boolean) {
909
909
  if (!viewer) return emptyCredites;
910
910
  const creditDisplay = viewer.creditDisplay as
911
911
  | (CreditDisplay & {
@@ -924,7 +924,14 @@ export function getCredits(viewer: Viewer) {
924
924
 
925
925
  const credits: Credits = {
926
926
  engine: {
927
- cesium: cesiumCredits?.html ? { html: cesiumCredits.html } : undefined,
927
+ // Only include Cesium-ion credit when Ion assets are actually in use.
928
+ // hasCesiumIonAsset === false means explicitly no Ion assets; undefined preserves existing behavior.
929
+ cesium:
930
+ hasCesiumIonAsset === false
931
+ ? undefined
932
+ : cesiumCredits?.html
933
+ ? { html: cesiumCredits.html }
934
+ : undefined,
928
935
  },
929
936
  lightbox: Array.from(lightboxCredits?._array ?? []).map(c => ({
930
937
  html: c?.credit?.html,
@@ -51,6 +51,7 @@ import {
51
51
  export default function useEngineRef(
52
52
  ref: Ref<EngineRef>,
53
53
  cesium: RefObject<CesiumComponentRef<Cesium.Viewer> | null>,
54
+ hasCesiumIonAssetRef?: RefObject<boolean | undefined>,
54
55
  ): EngineRef {
55
56
  const cancelCameraFlight = useRef<() => void>(undefined);
56
57
  const mouseEventCallbacks = useRef<MouseEventCallbacks>({
@@ -983,10 +984,10 @@ export default function useEngineRef(
983
984
  getCredits: () => {
984
985
  const viewer = cesium.current?.cesiumElement;
985
986
  if (!viewer || viewer.isDestroyed()) return;
986
- return getCredits(viewer);
987
+ return getCredits(viewer, hasCesiumIonAssetRef?.current);
987
988
  },
988
989
  };
989
- }, [cesium]);
990
+ }, [cesium, hasCesiumIonAssetRef]);
990
991
 
991
992
  useImperativeHandle(ref, () => e, [e]);
992
993
 
@@ -92,6 +92,7 @@ export default ({
92
92
  onCameraChange,
93
93
  onMount,
94
94
  onCreditsUpdate,
95
+ hasCesiumIonAsset,
95
96
  }: {
96
97
  ref: React.ForwardedRef<EngineRef>;
97
98
  property?: ViewerProperty;
@@ -133,6 +134,7 @@ export default ({
133
134
  onCameraChange?: (camera: Camera) => void;
134
135
  onMount?: () => void;
135
136
  onCreditsUpdate?: (credits: Credits) => void;
137
+ hasCesiumIonAsset?: boolean;
136
138
  }) => {
137
139
  const cesium = useRef<CesiumComponentRef<CesiumViewer>>(null);
138
140
 
@@ -141,8 +143,11 @@ export default ({
141
143
  ? meta.cesiumIonAccessToken
142
144
  : undefined;
143
145
 
146
+ const hasCesiumIonAssetRef = useRef(hasCesiumIonAsset);
147
+ hasCesiumIonAssetRef.current = hasCesiumIonAsset;
148
+
144
149
  // expose ref
145
- const engineAPI = useEngineRef(ref, cesium);
150
+ const engineAPI = useEngineRef(ref, cesium, hasCesiumIonAssetRef);
146
151
 
147
152
  const layerSelectWithRectEventHandlers = useLayerSelectWithRect({
148
153
  cesium,
@@ -667,7 +672,7 @@ export default ({
667
672
  if (!onCreditsUpdateRef.current) return;
668
673
  const viewer = cesium.current?.cesiumElement;
669
674
  if (!viewer || viewer.isDestroyed()) return;
670
- const credits = getCredits(viewer);
675
+ const credits = getCredits(viewer, hasCesiumIonAssetRef.current);
671
676
  onCreditsUpdateRef.current(credits);
672
677
  }, 3000);
673
678
  }, []);
@@ -49,6 +49,7 @@ const Cesium: React.ForwardRefRenderFunction<EngineRef, EngineProps> = (
49
49
  meta,
50
50
  customProvider,
51
51
  displayCredits,
52
+ hasCesiumIonAsset,
52
53
  layersRef,
53
54
  featureFlags,
54
55
  requestingRenderMode,
@@ -119,6 +120,7 @@ const Cesium: React.ForwardRefRenderFunction<EngineRef, EngineProps> = (
119
120
  onCameraChange,
120
121
  onMount,
121
122
  onCreditsUpdate,
123
+ hasCesiumIonAsset,
122
124
  });
123
125
 
124
126
  const verticalExaggeration = property?.terrain?.enabled