Skip to content

Commit 8c69c40

Browse files
ZTongciairsliceCopilot
authored
feat: support custom provider (#128)
Co-authored-by: airslice <icesunex@hotmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent db45b59 commit 8c69c40

26 files changed

Lines changed: 1100 additions & 597 deletions

File tree

.env.example

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
1-
EXAMPLE_CESIUM_ION_ACCESS_TOKEN=your_access_token_here
2-
EXAMPLE_GOOGLE_MAP_API_KEY=your_google_map_api_key
1+
# Used by the google-photorealistic layer as a fallback when no custom Google 3D Tiles URL is configured
2+
EXAMPLE_GOOGLE_MAP_API_KEY=
3+
4+
# Cesium Ion access token — required to test cesium_ion_* imagery tiles and osm-buildings layer
5+
EXAMPLE_CESIUM_ION_ACCESS_TOKEN=
6+
7+
# Custom provider configuration — JSON object matching CustomProviderConfig type
8+
# Structure: { "imagery": { "providers": [...] } }
9+
# Each imagery provider: { "id": string, "url": string, "credit"?: string, "maximumLevel"?: number, "minimumLevel"?: number }
10+
# Note: Include authentication tokens directly in URLs if needed (e.g., ?token=xxx or ?key=yyy)
11+
# Example: {"imagery":{"providers":[{"id":"my_satellite","url":"https://tiles.example.com/{z}/{x}/{y}.png?token=abc123","credit":"© Example","maximumLevel":19}]}}
12+
EXAMPLE_CUSTOM_PROVIDER=

example/App.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ function App() {
1414
handleAPIReady,
1515
handleSelect,
1616
meta,
17+
customProvider,
18+
customTileIds,
1719
currentTile,
1820
setCurrentTile,
21+
cesiumIonAssetId,
22+
setCesiumIonAssetId,
1923
currentCamera,
2024
setCurrentCamera,
2125
terrainEnabled,
@@ -46,8 +50,11 @@ function App() {
4650
return (
4751
<div className="relative w-screen h-screen overflow-hidden">
4852
<OptionsPanel
53+
customTileIds={customTileIds}
4954
currentTile={currentTile}
5055
setCurrentTile={setCurrentTile}
56+
cesiumIonAssetId={cesiumIonAssetId}
57+
setCesiumIonAssetId={setCesiumIonAssetId}
5158
terrainEnabled={terrainEnabled}
5259
setTerrainEnabled={setTerrainEnabled}
5360
hideUnderground={hideUnderground}
@@ -78,6 +85,7 @@ function App() {
7885
onLayerSelect={handleSelect}
7986
engine="cesium"
8087
meta={meta}
88+
customProvider={customProvider}
8189
viewerProperty={isReady ? viewerProperty : undefined}
8290
camera={currentCamera}
8391
onCameraChange={setCurrentCamera}

example/components/OptionsPanel/index.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ import { SKETCH_TOOLS, TILES } from "@/constants";
2121
import { TEST_LAYERS } from "@/testLayers";
2222

2323
type OptionsPanelProps = {
24+
customTileIds?: string[];
2425
currentTile?: string;
2526
setCurrentTile: (v: string) => void;
27+
cesiumIonAssetId?: number;
28+
setCesiumIonAssetId: (v: number | undefined) => void;
2629
terrainEnabled: boolean;
2730
setTerrainEnabled: (v: boolean) => void;
2831
hideUnderground: boolean;
@@ -46,8 +49,11 @@ type OptionsPanelProps = {
4649
};
4750

4851
const OptionsPanel: FC<OptionsPanelProps> = ({
52+
customTileIds,
4953
currentTile,
5054
setCurrentTile,
55+
cesiumIonAssetId,
56+
setCesiumIonAssetId,
5157
terrainEnabled,
5258
setTerrainEnabled,
5359
hideUnderground,
@@ -92,13 +98,35 @@ const OptionsPanel: FC<OptionsPanelProps> = ({
9298
<SelectValue />
9399
</SelectTrigger>
94100
<SelectContent>
101+
{customTileIds?.map(tile => (
102+
<SelectItem key={tile} value={tile}>
103+
{tile}
104+
</SelectItem>
105+
))}
95106
{TILES.map(tile => (
96107
<SelectItem key={tile} value={tile}>
97108
{tile}
98109
</SelectItem>
99110
))}
100111
</SelectContent>
101112
</Select>
113+
{currentTile === "cesium_ion" && (
114+
<div className="flex items-center gap-2 mt-1">
115+
<label className="text-sm font-medium leading-none opacity-70 shrink-0">
116+
Asset ID
117+
</label>
118+
<input
119+
type="number"
120+
className="w-full border rounded px-2 py-1 text-sm"
121+
placeholder="e.g. 3812"
122+
value={cesiumIonAssetId ?? ""}
123+
onChange={e => {
124+
const v = parseInt(e.target.value, 10);
125+
setCesiumIonAssetId(isNaN(v) ? undefined : v);
126+
}}
127+
/>
128+
</div>
129+
)}
102130
</OptionSection>
103131

104132
<OptionSection title="Terrain">

example/constants.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,21 @@ export const DEFAULT_CAMERA = {
1212
roll: 6.2830631767616465,
1313
};
1414

15+
// Static preset tile types — always available regardless of CustomProviderConfig.
16+
// Dynamic provider IDs (from EXAMPLE_CUSTOM_PROVIDER) are appended at runtime in hooks.ts.
1517
export const TILES = [
16-
"default",
17-
"default_label",
18-
"default_road",
18+
// Public (always available)
1919
"open_street_map",
20-
"esri_world_topo",
21-
"black_marble",
2220
"japan_gsi_standard",
23-
"test_unexpected_type",
21+
"stamen_watercolor",
22+
"carto_light",
23+
// Cesium Ion — custom asset ID (enter asset ID in the input below)
24+
"cesium_ion",
25+
// Cesium Ion — preset assets (requires user-provided Ion token in scene settings)
26+
"cesium_ion_default",
27+
"cesium_ion_labelled",
28+
"cesium_ion_road",
29+
"cesium_ion_earth_at_night",
2430
];
2531

2632
export const SKETCH_TOOLS: SketchType[] = [

example/hooks.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ComputedFeature,
55
ComputedLayer,
66
Credits,
7+
CustomProviderConfig,
78
LayerSelectionReason,
89
LazyLayer,
910
MapRef,
@@ -46,18 +47,33 @@ export default () => {
4647
setSelectedFeature(ref.current?.layers.selectedFeature() ?? feature);
4748
}, []);
4849

50+
const customProvider = useMemo(() => {
51+
const envValue = import.meta.env.EXAMPLE_CUSTOM_PROVIDER;
52+
if (!envValue) return undefined;
53+
try {
54+
return JSON.parse(envValue) as CustomProviderConfig;
55+
} catch (error) {
56+
console.warn("[example] Failed to parse EXAMPLE_CUSTOM_PROVIDER:", error);
57+
return undefined;
58+
}
59+
}, []);
60+
4961
const meta = useMemo(
5062
() => ({
5163
cesiumIonAccessToken: import.meta.env.EXAMPLE_CESIUM_ION_ACCESS_TOKEN || undefined,
5264
}),
5365
[],
5466
);
5567

68+
const customTileIds = customProvider?.imagery?.providers?.map((p: { id: string }) => p.id) ?? [];
69+
70+
const allTileIds = [...TILES, ...customTileIds];
5671
const [currentTile, setCurrentTile] = useState(
57-
TILES.includes(DEFAULT_VIEWER_PROPERTY.tiles?.[0]?.type ?? "")
72+
allTileIds.includes(DEFAULT_VIEWER_PROPERTY.tiles?.[0]?.type ?? "")
5873
? DEFAULT_VIEWER_PROPERTY.tiles?.[0]?.type
5974
: undefined,
6075
);
76+
const [cesiumIonAssetId, setCesiumIonAssetId] = useState<number | undefined>(undefined);
6177
const [currentCamera, setCurrentCamera] = useState(DEFAULT_CAMERA);
6278
const [terrainEnabled, setTerrainEnabled] = useState(true);
6379
const [hideUnderground, setHideUnderground] = useState(false);
@@ -71,6 +87,7 @@ export default () => {
7187
{
7288
id: "default",
7389
type: currentTile,
90+
cesiumIonAssetId: currentTile === "cesium_ion" ? cesiumIonAssetId : undefined,
7491
opacity: 1,
7592
},
7693
]
@@ -84,7 +101,7 @@ export default () => {
84101
depthTestAgainstTerrain: hideUnderground,
85102
},
86103
}),
87-
[currentTile, terrainEnabled, hideUnderground],
104+
[currentTile, cesiumIonAssetId, terrainEnabled, hideUnderground],
88105
);
89106

90107
const layers = useMemo(
@@ -118,7 +135,10 @@ export default () => {
118135
!selectedFeature?.id
119136
)
120137
return;
121-
ref.current?.sketch.editFeature({ layerId: selectedLayer.id, feature: selectedFeature });
138+
ref.current?.sketch.editFeature({
139+
layerId: selectedLayer.id,
140+
feature: selectedFeature,
141+
});
122142
}, [selectedLayer, selectedFeature]);
123143

124144
const handleCancelEditSketchFeature = useCallback(() => {
@@ -165,8 +185,12 @@ export default () => {
165185
handleAPIReady,
166186
handleSelect,
167187
meta,
188+
customProvider,
189+
customTileIds,
168190
currentTile,
169191
setCurrentTile,
192+
cesiumIonAssetId,
193+
setCesiumIonAssetId,
170194
currentCamera,
171195
setCurrentCamera,
172196
terrainEnabled,

example/scene.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const DEFAULT_VIEWER_PROPERTY: ViewerProperty = {
6262
terrain: {
6363
enabled: true,
6464
normal: true,
65+
type: "reearth_terrain",
6566
},
6667
geoid: {
6768
server: {

example/testLayers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { GEOJSON_SIMPLE } from "./geojson_simple";
66
import { GOOGLE_PHOTOREALISTIC_3DTILES } from "./google_photorealistic_3dtiles";
77
import { LAND_USE, LSLD_SAPPORO, LSLD_NIJIMA } from "./mvt";
88
import { OSM_BUILDINGS } from "./osm_buildings";
9+
import { REEARTH_BUILDINGS } from "./reearth_buildings";
910
import { THREEDTILES_KUMAGAYA_LOD2 } from "./threedtiles_lod2";
1011
import { THREEDTILES_SIMPLE } from "./threedtiles_simple";
1112
import { THREEDTILES_SIMPLE_WITH_STYLE_URL } from "./threedtiles_simple_with_style_url";
@@ -18,6 +19,7 @@ export const TEST_LAYERS: Layer[] = [
1819
GEOJSON_SIMPLE,
1920
GOOGLE_PHOTOREALISTIC_3DTILES,
2021
OSM_BUILDINGS,
22+
REEARTH_BUILDINGS,
2123
THREEDTILES_SIMPLE,
2224
THREEDTILES_SIMPLE_WITH_STYLE_URL,
2325
THREEDTILES_KUMAGAYA_LOD2,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Layer } from "@reearth/core";
2+
3+
export const REEARTH_BUILDINGS: Layer = {
4+
id: "reearth_buildings",
5+
type: "simple",
6+
data: {
7+
type: "reearth-buildings",
8+
},
9+
"3dtiles": {},
10+
};

0 commit comments

Comments
 (0)