Skip to content

Commit

Permalink
feat(web): support cesium and google photorealistic 3d tiles (#1172)
Browse files Browse the repository at this point in the history
Co-authored-by: airslice <[email protected]>
  • Loading branch information
mkumbobeaty and airslice authored Oct 17, 2024
1 parent ec10ce2 commit b96d9f3
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 43 deletions.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
"@lexical/utils": "0.12.0",
"@monaco-editor/react": "4.6.0",
"@popperjs/core": "2.11.8",
"@reearth/core": "0.0.7-alpha.15",
"@reearth/core": "0.0.7-alpha.17",
"@rot1024/use-transition": "1.0.0",
"@sentry/browser": "7.77.0",
"@seznam/compose-react-refs": "1.0.6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,124 @@ import {
SubmitWrapper,
Wrapper,
InputsWrapper,
ContentWrapper
ContentWrapper,
LinkWrapper
} from "@reearth/beta/features/Editor/Map/shared/SharedComponent";
import { Button, TextInput } from "@reearth/beta/lib/reearth-ui";
import { Button, RadioGroup, TextInput } from "@reearth/beta/lib/reearth-ui";
import { useT } from "@reearth/services/i18n";
import { FC, useState } from "react";
import { FC, useCallback, useMemo, useState } from "react";

import { DataProps } from "..";
import { DataProps, DataSourceOptType, SourceType } from "..";
import { generateTitle } from "../util";

const ThreeDTiles: FC<DataProps> = ({ sceneId, onSubmit, onClose }) => {
const t = useT();

const [value, setValue] = useState("");
const [sourceType, setSourceType] = useState<SourceType>("osm-buildings");
const [googleMapsApiKey, setGoogleMapsApiKey] = useState("");
const googlePhotorealistic = sourceType === "google-photorealistic";

const renderGooglePhotorealisticInput = useMemo(() => {
if (googlePhotorealistic) {
return (
<InputGroup
label={
<>
{t("Google Maps API Key ")} ( {t("You can apply a key ")}
<LinkWrapper
to="https://developers.google.com/maps/documentation/javascript/get-api-key"
target="_blank"
rel="noopener noreferrer"
>
{t("here")}
</LinkWrapper>
)
</>
}
>
<InputsWrapper>
<TextInput
value={googleMapsApiKey}
onChange={(value) => setGoogleMapsApiKey(value)}
/>
</InputsWrapper>
</InputGroup>
);
} else return undefined;
}, [googleMapsApiKey, googlePhotorealistic, t]);

const renderUrlInput = useMemo(() => {
if (sourceType === "url") {
return (
<InputGroup label={t("Resource URL")}>
<InputsWrapper>
<TextInput
placeholder="https://"
value={value}
onChange={(value) => setValue(value)}
/>
</InputsWrapper>
</InputGroup>
);
} else return undefined;
}, [sourceType, t, value]);

const dataSourceOptions: DataSourceOptType = useMemo(
() => [
{ label: t("Cesium OSM 3D Tiles"), value: "osm-buildings" },
{
label: t("Google Photorealistic 3D Tiles"),
value: "google-photorealistic",
children: renderGooglePhotorealisticInput
},
{
label: t("URL"),
value: "url",
children: renderUrlInput
}
],
[renderGooglePhotorealisticInput, renderUrlInput, t]
);

const handleDataSourceTypeChange = useCallback((newValue: string) => {
setSourceType(newValue as SourceType);
setValue("");
setGoogleMapsApiKey("");
}, []);

const title = useMemo(() => {
if (googlePhotorealistic) {
return t("Google Photorealistic 3D Tiles");
} else if (sourceType === "osm-buildings") {
return t("Cesium OSM 3D Tiles");
} else {
return generateTitle(value);
}
}, [googlePhotorealistic, sourceType, t, value]);

const handleSubmit = () => {
onSubmit({
layerType: "simple",
sceneId,
title: generateTitle(value),
title,
visible: true,
config: {
data: {
url: value !== "" ? value : undefined,
type: "3dtiles"
...(googlePhotorealistic
? {
serviceTokens: {
googleMapApiKey: googleMapsApiKey || undefined
}
}
: {}),
type:
sourceType === "osm-buildings"
? "osm-buildings"
: googlePhotorealistic
? "google-photorealistic"
: "3dtiles"
}
}
});
Expand All @@ -35,23 +130,22 @@ const ThreeDTiles: FC<DataProps> = ({ sceneId, onSubmit, onClose }) => {
return (
<Wrapper>
<ContentWrapper>
<InputGroup label={t("Resource URL")}>
<InputsWrapper>
<TextInput
placeholder="https://"
value={value}
onChange={(value) => setValue(value)}
/>
</InputsWrapper>
</InputGroup>
<RadioGroup
value={sourceType}
layout="vertical"
options={dataSourceOptions}
onChange={handleDataSourceTypeChange}
/>
</ContentWrapper>

<SubmitWrapper>
<Button
title={t("Add to Layer")}
appearance="primary"
onClick={handleSubmit}
disabled={!value}
disabled={
(!value && sourceType === "url") ||
(!googleMapsApiKey && googlePhotorealistic)
}
/>
</SubmitWrapper>
</Wrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Modal, ModalPanel, TabItem, Tabs } from "@reearth/beta/lib/reearth-ui";
import { useT } from "@reearth/services/i18n";
import { styled } from "@reearth/services/theme";
import { FC } from "react";
import { FC, ReactNode } from "react";

import { LayerAddProps } from "../../hooks/useLayers";

Expand All @@ -17,11 +17,17 @@ export type DataProps = {
onSubmit: (layerAddInp: LayerAddProps) => void;
};

export type SourceType = "url" | "local" | "value";
export type SourceType =
| "url"
| "local"
| "value"
| "osm-buildings"
| "google-photorealistic";

export type DataSourceOptType = {
label: string;
value: SourceType;
children?: ReactNode;
}[];

const DataSourceLayerCreator: FC<DataProps> = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ const InspectorTabs: FC<Props> = ({
if (!selectedLayer?.computedFeature?.id) return;
const { id, geometry, properties } =
selectedLayer.layer?.config?.data?.type === "3dtiles" ||
selectedLayer.layer?.config?.data?.type === "osm-buildings" ||
selectedLayer.layer?.config?.data?.type === "google-photorealistic" ||
selectedLayer.layer?.config?.data?.type === "mvt"
? selectedLayer.computedFeature
: (selectedLayer.computedLayer?.features?.find(
Expand Down
9 changes: 8 additions & 1 deletion web/src/beta/features/Editor/Map/shared/SharedComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { styled } from "@reearth/services/theme";
import { FC, ReactNode } from "react";
import { Link } from "react-router-dom";

export const InputGroup: FC<{
label: string;
label: string | ReactNode;
description?: string;
children: ReactNode;
}> = ({ label, description, children }) => {
Expand Down Expand Up @@ -96,3 +97,9 @@ export const ContentWrapper = styled("div")(({ theme }) => ({
background: theme.relative.lighter
}
}));

export const LinkWrapper = styled(Link)(({ theme }) => ({
textDecoration: "none",
color: theme.select.strong,
paddingRight: theme.spacing.micro
}));
1 change: 1 addition & 0 deletions web/src/beta/features/Visualizer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ const Visualizer: FC<VisualizerProps> = ({
camera={visualizerCamera}
interactionMode={interactionMode}
shouldRender={shouldRender}
displayCredits={false}
onCameraChange={onCameraChange}
onLayerSelect={handleCoreLayerSelect}
onLayerDrop={handleLayerDrop}
Expand Down
45 changes: 31 additions & 14 deletions web/src/beta/lib/reearth-ui/components/Radio/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { styled } from "@reearth/services/theme";
import { FC } from "react";
import { FC, ReactNode } from "react";

export type RadioProps = {
value?: string;
label?: string;
disabled?: boolean;
checked?: boolean;
content?: ReactNode;
onChange?: (value: string) => void;
};

Expand All @@ -14,6 +15,7 @@ export const Radio: FC<RadioProps> = ({
label,
disabled,
checked,
content,
onChange
}) => {
const handleChange = () => {
Expand All @@ -22,22 +24,32 @@ export const Radio: FC<RadioProps> = ({
};

return (
<RadioWrapper onClick={handleChange}>
<RadioInput
type="radio"
value={value}
checked={checked}
disabled={disabled}
onChange={handleChange}
/>
<RadioButton checked={checked} disabled={disabled}>
{checked && !disabled && <RadioIndicator checked={checked} />}
</RadioButton>
{label && <RadioLabel disabled={disabled}>{label}</RadioLabel>}
</RadioWrapper>
<Wrapper>
<RadioWrapper onClick={handleChange}>
<RadioInput
type="radio"
value={value}
checked={checked}
disabled={disabled}
onChange={handleChange}
/>
<RadioButton checked={checked} disabled={disabled}>
{checked && !disabled && <RadioIndicator checked={checked} />}
</RadioButton>
{label && <RadioLabel disabled={disabled}>{label}</RadioLabel>}
</RadioWrapper>
{content && <RadioContent>{content}</RadioContent>}
</Wrapper>
);
};

const Wrapper = styled("div")(({ theme }) => ({
display: "flex",
gap: theme.spacing.smallest,
flexDirection: "column",
alignItems: "flex-start"
}));

const RadioWrapper = styled("div")(({ theme }) => ({
display: "flex",
gap: theme.spacing.smallest,
Expand Down Expand Up @@ -87,3 +99,8 @@ const RadioButton = styled("div")<{ checked?: boolean; disabled?: boolean }>(
cursor: disabled ? "not-allowed" : "pointer"
})
);

const RadioContent = styled("div")(({ theme }) => ({
padding: `${theme.spacing.small}px ${theme.spacing.large}px 0 ${theme.spacing.large}px`,
width: "100%"
}));
8 changes: 5 additions & 3 deletions web/src/beta/lib/reearth-ui/components/RadioGroup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Radio } from "@reearth/beta/lib/reearth-ui";
import { styled } from "@reearth/services/theme";
import { FC, useCallback, useEffect, useState } from "react";
import { FC, ReactNode, useCallback, useEffect, useState } from "react";

export type RadioGroupProps = {
layout?: "vertical" | "horizontal";
value?: string;
options?: { value: string; label?: string }[];
options?: { value: string; label?: string; children?: ReactNode }[];
onChange?: (value: string) => void;
};

Expand Down Expand Up @@ -38,6 +38,7 @@ export const RadioGroup: FC<RadioGroupProps> = ({
value={option.value}
label={option.label}
checked={option.value === currentValue}
content={option.children}
onChange={handleValueChange}
/>
))}
Expand All @@ -49,6 +50,7 @@ const RadioGroupWrapper = styled("div")<{ layout?: "vertical" | "horizontal" }>(
({ layout, theme }) => ({
display: "flex",
flexDirection: layout === "vertical" ? "column" : "row",
gap: theme.spacing.normal
gap: theme.spacing.normal,
width: "100%"
})
);
6 changes: 6 additions & 0 deletions web/src/services/i18n/translations/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ WMS: ''
Vector Tile: ''
3D Tiles: ''
Data Source Manager: ''
'Google Maps API Key ': ''
'You can apply a key ': ''
here: ''
Cesium OSM 3D Tiles: ''
Google Photorealistic 3D Tiles: ''
URL: ''
Choose layer to add: ''
layer name: ''
Layer name: ''
Expand Down
6 changes: 6 additions & 0 deletions web/src/services/i18n/translations/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ WMS: WMS
Vector Tile: Vector Tile
3D Tiles: 3D Tiles
Data Source Manager: データソースマネージャー
'Google Maps API Key ': ''
'You can apply a key ': ''
here: ''
Cesium OSM 3D Tiles: ''
Google Photorealistic 3D Tiles: ''
URL: ''
Choose layer to add: レイヤーの選択
layer name: レイヤー名
Layer name: ''
Expand Down
Loading

0 comments on commit b96d9f3

Please sign in to comment.