Skip to content

Commit

Permalink
feat(web): make it possible to change tiles and terrain on the asset …
Browse files Browse the repository at this point in the history
…preview (#1033)

* add: custom dropdown

* add: default terrain

* add: reflect settings

* fix: dropdown styling

* fix: missing type

* add: no image

* fix: mistype
  • Loading branch information
caichi-t authored Jan 24, 2024
1 parent 0e3f2df commit 377ba42
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 142 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 36 additions & 15 deletions web/src/components/atoms/ResiumViewer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import {
Cesium3DTileFeature,
createWorldTerrain,
Viewer as CesiumViewer,
JulianDate,
Entity,
} from "cesium";
import styled from "@emotion/styled";
import { Cesium3DTileFeature, Viewer as CesiumViewer, JulianDate, Entity } from "cesium";
import { ComponentProps, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { CesiumComponentRef, CesiumMovementEvent, RootEventTarget, Viewer } from "resium";

import InfoBox from "@reearth-cms/components/molecules/Asset/InfoBox";
import { WorkspaceSettings } from "@reearth-cms/components/molecules/Workspace/types";

import { imageryGet, terrainGet } from "./provider";
import { sortProperties } from "./sortProperty";

type Props = {
Expand All @@ -18,6 +15,7 @@ type Props = {
properties?: any;
showDescription?: boolean;
onSelect?: (id: string | undefined) => void;
workspaceSettings?: WorkspaceSettings;
} & ComponentProps<typeof Viewer>;

const ResiumViewer: React.FC<Props> = ({
Expand All @@ -26,6 +24,7 @@ const ResiumViewer: React.FC<Props> = ({
properties: passedProps,
showDescription,
onSelect,
workspaceSettings,
...props
}) => {
const viewer = useRef<CesiumComponentRef<CesiumViewer>>(null);
Expand Down Expand Up @@ -75,8 +74,6 @@ const ResiumViewer: React.FC<Props> = ({
return sortProperties(passedProps ?? properties);
}, [passedProps, properties]);

const terrainProvider = useMemo(() => createWorldTerrain(), []);

useEffect(() => {
if (viewer.current) {
onGetViewer(viewer.current?.cesiumElement);
Expand All @@ -88,15 +85,26 @@ const ResiumViewer: React.FC<Props> = ({
setInfoBoxVisibility(true);
}, []);

const imagery = useMemo(() => {
return workspaceSettings?.tiles ? imageryGet(workspaceSettings.tiles.resources) : [];
}, [workspaceSettings?.tiles]);

const terrain = useMemo(() => {
return workspaceSettings?.terrains?.enabled
? terrainGet(workspaceSettings.terrains.resources)
: [];
}, [workspaceSettings?.terrains]);

return (
<div style={{ position: "relative" }}>
<Viewer
terrainProvider={terrainProvider}
<Container>
<StyledViewer
navigationHelpButton={false}
homeButton={false}
projectionPicker={false}
sceneModePicker={false}
baseLayerPicker={false}
baseLayerPicker={true}
imageryProviderViewModels={imagery}
terrainProviderViewModels={terrain}
fullscreenButton={false}
vrButton={false}
selectionIndicator={false}
Expand All @@ -110,16 +118,29 @@ const ResiumViewer: React.FC<Props> = ({
ref={viewer}
{...props}>
{children}
</Viewer>
</StyledViewer>
<InfoBox
infoBoxProps={sortedProperties}
infoBoxVisibility={infoBoxVisibility && !!selected}
title={title}
description={description}
onClose={handleClose}
/>
</div>
</Container>
);
};

export default ResiumViewer;

const Container = styled.div`
position: relative;
`;

const StyledViewer = styled(Viewer)`
.cesium-baseLayerPicker-dropDown {
box-sizing: content-box;
}
.cesium-baseLayerPicker-choices {
text-align: left;
}
`;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
230 changes: 230 additions & 0 deletions web/src/components/atoms/ResiumViewer/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import {
ProviderViewModel,
ArcGisMapServerImageryProvider,
OpenStreetMapImageryProvider,
ArcGISTiledElevationTerrainProvider,
UrlTemplateImageryProvider,
CesiumTerrainProvider,
IonResource,
EllipsoidTerrainProvider,
createWorldTerrainAsync,
buildModuleUrl,
createWorldImageryAsync,
IonWorldImageryStyle,
IonImageryProvider,
} from "cesium";

import {
TileResource,
TerrainResource,
UrlResourceProps,
CesiumResourceProps,
} from "@reearth-cms/components/molecules/Workspace/types";

import ArcgisThumbnail from "./arcgisThumbnail.png";
import NoImage from "./noImage.jpg";

const accessToken = window.REEARTH_CONFIG?.cesiumIonAccessToken;

const defaultTile = new ProviderViewModel({
name: "Default",
iconUrl: buildModuleUrl("Widgets/Images/ImageryProviders/bingAerial.png"),
tooltip: "",
creationFunction: () => {
return createWorldImageryAsync({
style: IonWorldImageryStyle.AERIAL,
}) as any;
},
});

const labelled = new ProviderViewModel({
name: "Labelled",
iconUrl: buildModuleUrl("Widgets/Images/ImageryProviders/bingAerialLabels.png"),
tooltip: "",
creationFunction: () => {
return createWorldImageryAsync({
style: IonWorldImageryStyle.AERIAL_WITH_LABELS,
}) as any;
},
});

const roadMap = new ProviderViewModel({
name: "RoadMap",
iconUrl: buildModuleUrl("Widgets/Images/ImageryProviders/bingRoads.png"),
tooltip: "",
creationFunction: () => {
return createWorldImageryAsync({
style: IonWorldImageryStyle.ROAD,
}) as any;
},
});

const openStreetMap = new ProviderViewModel({
name: "OpenStreetMap",
iconUrl: buildModuleUrl("Widgets/Images/ImageryProviders/openStreetMap.png"),
tooltip: "",
creationFunction: () => {
return new OpenStreetMapImageryProvider({
url: "https://a.tile.openstreetmap.org/",
});
},
});

const esriTopography = new ProviderViewModel({
name: "ESRI Topography",
iconUrl:
"https://services.arcgisonline.com/arcgis/rest/services/World_Topo_Map/MapServer/tile/0/0/0",
tooltip: "",
creationFunction: () => {
return new ArcGisMapServerImageryProvider({
url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",
credit:
"Copyright: Tiles © Esri — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Communit",
enablePickFeatures: false,
});
},
});

const earthAtNight = new ProviderViewModel({
name: "Earth at night",
iconUrl: buildModuleUrl("Widgets/Images/ImageryProviders/earthAtNight.png"),
tooltip: "",
creationFunction: () => {
return IonImageryProvider.fromAssetId(3812, {}) as any;
},
});

const japanGsi = new ProviderViewModel({
name: "Japan GSI Standard Map",
iconUrl: "https://maps.gsi.go.jp/xyz/std/0/0/0.png",
tooltip: "",
creationFunction: () => {
return new OpenStreetMapImageryProvider({
url: "https://cyberjapandata.gsi.go.jp/xyz/std/",
});
},
});

const urlGet = ({ name, url, image }: UrlResourceProps) => {
return new ProviderViewModel({
name,
iconUrl: image || NoImage,
tooltip: "",
creationFunction: () => {
return new UrlTemplateImageryProvider({
url,
});
},
});
};

export const imageryGet = (tiles: TileResource[]) => {
const result: ProviderViewModel[] = [];
tiles.forEach(tile => {
switch (tile.type) {
case "LABELLED":
result.push(labelled);
break;
case "ROAD_MAP":
result.push(roadMap);
break;
case "OPEN_STREET_MAP":
result.push(openStreetMap);
break;
case "ESRI_TOPOGRAPHY":
result.push(esriTopography);
break;
case "EARTH_AT_NIGHT":
result.push(earthAtNight);
break;
case "JAPAN_GSI_STANDARD_MAP":
result.push(japanGsi);
break;
case "URL": {
const url = tile.props.url;
if (url) result.push(urlGet(tile.props));
break;
}
default:
result.push(defaultTile);
break;
}
});
if (result.length === 0) result.push(defaultTile);
return result;
};

const ellipsoid = new ProviderViewModel({
name: "WGS84 Ellipsoid",
iconUrl: buildModuleUrl("Widgets/Images/TerrainProviders/Ellipsoid.png"),
tooltip: "",
creationFunction: () => {
return new EllipsoidTerrainProvider();
},
});

const cesiumWorld = new ProviderViewModel({
name: "Cesium World Terrain",
iconUrl: buildModuleUrl("Widgets/Images/TerrainProviders/CesiumWorldTerrain.png"),
tooltip: "",
creationFunction: () => {
return createWorldTerrainAsync({
requestWaterMask: true,
requestVertexNormals: true,
});
},
});

const arcGis = new ProviderViewModel({
name: "ArcGIS Terrain",
iconUrl: ArcgisThumbnail,
tooltip: "",
creationFunction: () => {
return new ArcGISTiledElevationTerrainProvider({
url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
});
},
});

const cesiumIonGet = ({
name,
url,
image,
cesiumIonAssetId,
cesiumIonAccessToken,
}: CesiumResourceProps) => {
return new ProviderViewModel({
name,
iconUrl: image || NoImage,
tooltip: "",
creationFunction: () => {
return new CesiumTerrainProvider({
url:
url ||
IonResource.fromAssetId(parseInt(cesiumIonAssetId, 10), {
accessToken: cesiumIonAccessToken || accessToken,
}),
});
},
});
};

export const terrainGet = (terrains: TerrainResource[]) => {
const result: ProviderViewModel[] = [];
result.push(ellipsoid);
terrains.forEach(terrain => {
switch (terrain.type) {
case "ARC_GIS_TERRAIN":
result.push(arcGis);
break;
case "CESIUM_ION": {
result.push(cesiumIonGet(terrain.props));
break;
}
default:
result.push(cesiumWorld);
break;
}
});
return result;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Viewer as CesiumViewer } from "cesium";
import { ComponentProps, useEffect } from "react";

import ResiumViewer from "@reearth-cms/components/atoms/ResiumViewer";
import Settings from "@reearth-cms/components/molecules/Asset/Viewers/Settings";
import { compressedFileFormats } from "@reearth-cms/components/molecules/Common/Asset";
import { WorkspaceSettings } from "@reearth-cms/components/molecules/Workspace/types";
import { getExtension } from "@reearth-cms/utils/file";
Expand Down Expand Up @@ -34,9 +33,8 @@ const Geo3dViewer: React.FC<Props> = ({
}, [setAssetUrl, url]);

return (
<ResiumViewer {...viewerProps} onGetViewer={onGetViewer}>
<ResiumViewer {...viewerProps} onGetViewer={onGetViewer} workspaceSettings={workspaceSettings}>
<Cesium3dTileSetComponent url={url} />
<Settings workspaceSettings={workspaceSettings} />
</ResiumViewer>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Viewer as CesiumViewer } from "cesium";
import { ComponentProps, useCallback } from "react";

import ResiumViewer from "@reearth-cms/components/atoms/ResiumViewer";
import Settings from "@reearth-cms/components/molecules/Asset/Viewers/Settings";
import { WorkspaceSettings } from "@reearth-cms/components/molecules/Workspace/types";
import { getExtension } from "@reearth-cms/utils/file";

Expand Down Expand Up @@ -40,9 +39,12 @@ const GeoViewer: React.FC<Props> = ({
}, [ext, url]);

return (
<ResiumViewer showDescription={ext === "czml"} {...viewerProps} onGetViewer={onGetViewer}>
<ResiumViewer
showDescription={ext === "czml"}
{...viewerProps}
onGetViewer={onGetViewer}
workspaceSettings={workspaceSettings}>
{renderAsset()}
<Settings workspaceSettings={workspaceSettings} />
</ResiumViewer>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Viewer as CesiumViewer } from "cesium";
import { ComponentProps } from "react";

import ResiumViewer from "@reearth-cms/components/atoms/ResiumViewer";
import Settings from "@reearth-cms/components/molecules/Asset/Viewers/Settings";
import { WorkspaceSettings } from "@reearth-cms/components/molecules/Workspace/types";

import { Imagery } from "./Imagery";
Expand All @@ -16,9 +15,8 @@ type Props = {

const GltfViewer: React.FC<Props> = ({ viewerProps, url, onGetViewer, workspaceSettings }) => {
return (
<ResiumViewer {...viewerProps} onGetViewer={onGetViewer}>
<ResiumViewer {...viewerProps} onGetViewer={onGetViewer} workspaceSettings={workspaceSettings}>
<Imagery url={url} />
<Settings workspaceSettings={workspaceSettings} />
</ResiumViewer>
);
};
Expand Down
Loading

0 comments on commit 377ba42

Please sign in to comment.