From 1d35d9d8cec38ae33b3757bb1d17b36266edf7ce Mon Sep 17 00:00:00 2001 From: keiya sasaki Date: Thu, 14 Dec 2023 14:19:36 +0900 Subject: [PATCH] chore(web): extend camera and marker APIs for pedestrian (#859) --- web/src/beta/lib/core/Crust/Plugins/api.ts | 9 ++++ web/src/beta/lib/core/Crust/Plugins/hooks.ts | 45 +++++++++++++++++++ .../lib/core/Crust/Plugins/plugin_types.ts | 13 ++++++ .../beta/lib/core/Crust/Plugins/storybook.tsx | 4 ++ web/src/beta/lib/core/Map/ref.ts | 3 ++ web/src/beta/lib/core/Map/types/index.ts | 13 ++++++ .../engines/Cesium/Feature/Marker/index.tsx | 20 +++++++-- .../beta/lib/core/engines/Cesium/common.ts | 12 +++-- .../lib/core/engines/Cesium/useEngineRef.ts | 29 ++++++++++++ web/src/beta/lib/core/mantle/compat/types.ts | 2 + .../beta/lib/core/mantle/types/appearance.ts | 2 + web/src/beta/lib/core/mantle/types/value.ts | 1 + web/src/beta/lib/core/types.ts | 2 + 13 files changed, 149 insertions(+), 6 deletions(-) diff --git a/web/src/beta/lib/core/Crust/Plugins/api.ts b/web/src/beta/lib/core/Crust/Plugins/api.ts index e9a0e9b97..ef0bff915 100644 --- a/web/src/beta/lib/core/Crust/Plugins/api.ts +++ b/web/src/beta/lib/core/Crust/Plugins/api.ts @@ -428,6 +428,9 @@ export function commonReearth({ captureScreen, getLocationFromScreen, sampleTerrainHeight, + computeGlobeHeight, + toXYZ, + toLngLatHeight, enableScreenSpaceCameraController, lookHorizontal, lookVertical, @@ -475,6 +478,9 @@ export function commonReearth({ captureScreen: GlobalThis["reearth"]["scene"]["captureScreen"]; getLocationFromScreen: GlobalThis["reearth"]["scene"]["getLocationFromScreen"]; sampleTerrainHeight: GlobalThis["reearth"]["scene"]["sampleTerrainHeight"]; + computeGlobeHeight: GlobalThis["reearth"]["scene"]["computeGlobeHeight"]; + toXYZ: GlobalThis["reearth"]["scene"]["toXYZ"]; + toLngLatHeight: GlobalThis["reearth"]["scene"]["toLngLatHeight"]; inEditor: () => GlobalThis["reearth"]["scene"]["inEditor"]; built: () => GlobalThis["reearth"]["scene"]["built"]; enableScreenSpaceCameraController: GlobalThis["reearth"]["camera"]["enableScreenSpaceController"]; @@ -548,6 +554,9 @@ export function commonReearth({ captureScreen, getLocationFromScreen, sampleTerrainHeight, + computeGlobeHeight, + toXYZ, + toLngLatHeight, pickManyFromViewport, }, get viewport() { diff --git a/web/src/beta/lib/core/Crust/Plugins/hooks.ts b/web/src/beta/lib/core/Crust/Plugins/hooks.ts index 2bb8fb1d7..44ece12f5 100644 --- a/web/src/beta/lib/core/Crust/Plugins/hooks.ts +++ b/web/src/beta/lib/core/Crust/Plugins/hooks.ts @@ -228,6 +228,45 @@ export default function ({ [engineRef], ); + const computeGlobeHeight = useCallback( + (lng: number, lat: number, height?: number) => { + return engineRef?.computeGlobeHeight(lng, lat, height); + }, + [engineRef], + ); + + const toXYZ = useCallback( + ( + lng: number, + lat: number, + height: number, + options?: + | { + useGlobeEllipsoid?: boolean | undefined; + } + | undefined, + ) => { + return engineRef?.toXYZ(lng, lat, height, options); + }, + [engineRef], + ); + + const toLngLatHeight = useCallback( + ( + x: number, + y: number, + z: number, + options?: + | { + useGlobeEllipsoid?: boolean | undefined; + } + | undefined, + ) => { + return engineRef?.toLngLatHeight(x, y, z, options); + }, + [engineRef], + ); + const enableScreenSpaceCameraController = useCallback( (enabled: boolean) => engineRef?.enableScreenSpaceCameraController(enabled), [engineRef], @@ -404,6 +443,9 @@ export default function ({ zoomOut, cameraViewport, getCameraFovInfo, + computeGlobeHeight, + toXYZ, + toLngLatHeight, rotateRight, orbit, captureScreen, @@ -466,6 +508,9 @@ export default function ({ getLocationFromScreen, sampleTerrainHeight, enableScreenSpaceCameraController, + computeGlobeHeight, + toXYZ, + toLngLatHeight, lookHorizontal, lookVertical, moveForward, diff --git a/web/src/beta/lib/core/Crust/Plugins/plugin_types.ts b/web/src/beta/lib/core/Crust/Plugins/plugin_types.ts index 604a843e7..ef6f70fbd 100644 --- a/web/src/beta/lib/core/Crust/Plugins/plugin_types.ts +++ b/web/src/beta/lib/core/Crust/Plugins/plugin_types.ts @@ -128,6 +128,19 @@ export type Scene = { withTerrain?: boolean, ) => LatLngHeight | undefined; readonly sampleTerrainHeight: (lng: number, lat: number) => Promise; + readonly computeGlobeHeight: (lng: number, lat: number, height?: number) => number | undefined; + readonly toXYZ: ( + lng: number, + lat: number, + height: number, + options?: { useGlobeEllipsoid?: boolean }, + ) => [x: number, y: number, z: number] | undefined; + readonly toLngLatHeight: ( + x: number, + y: number, + z: number, + options?: { useGlobeEllipsoid?: boolean }, + ) => [lng: number, lat: number, height: number] | undefined; readonly pickManyFromViewport: ( windowPosition: [x: number, y: number], windowWidth: number, diff --git a/web/src/beta/lib/core/Crust/Plugins/storybook.tsx b/web/src/beta/lib/core/Crust/Plugins/storybook.tsx index f04e6ca2a..59aaa85a3 100644 --- a/web/src/beta/lib/core/Crust/Plugins/storybook.tsx +++ b/web/src/beta/lib/core/Crust/Plugins/storybook.tsx @@ -94,6 +94,9 @@ export const context: Context = { captureScreen: act("captureScreen"), getLocationFromScreen: act("getLocationFromScreen"), sampleTerrainHeight: act("sampleTerrainHeight"), + computeGlobeHeight: act("computeGlobeHeight"), + toXYZ: act("toXYZ"), + toLngLatHeight: act("toLngLatHeight"), pickManyFromViewport: act("pickManyFromViewport"), }, layers: { @@ -124,6 +127,7 @@ export const context: Context = { pitch: 0, roll: 0, fov: Math.PI * (60 / 180), + aspectRatio: 1, }, viewport: { west: 0, east: 0, north: 0, south: 0 }, getFovInfo: act("getFovInfo"), diff --git a/web/src/beta/lib/core/Map/ref.ts b/web/src/beta/lib/core/Map/ref.ts index 50c6dfe6b..bfc115f4d 100644 --- a/web/src/beta/lib/core/Map/ref.ts +++ b/web/src/beta/lib/core/Map/ref.ts @@ -23,6 +23,9 @@ const engineRefKeys: FunctionKeys = { getClock: 1, getLocationFromScreen: 1, sampleTerrainHeight: 1, + computeGlobeHeight: 1, + toXYZ: 1, + toLngLatHeight: 1, getViewport: 1, lookAt: 1, lookAtLayer: 1, diff --git a/web/src/beta/lib/core/Map/types/index.ts b/web/src/beta/lib/core/Map/types/index.ts index b16818301..41bb422df 100644 --- a/web/src/beta/lib/core/Map/types/index.ts +++ b/web/src/beta/lib/core/Map/types/index.ts @@ -73,6 +73,19 @@ export type EngineRef = { | undefined; getLocationFromScreen: (x: number, y: number, withTerrain?: boolean) => LatLngHeight | undefined; sampleTerrainHeight: (lng: number, lat: number) => Promise; + computeGlobeHeight: (lng: number, lat: number, height?: number) => number | undefined; + toXYZ: ( + lng: number, + lat: number, + height: number, + options?: { useGlobeEllipsoid?: boolean }, + ) => [x: number, y: number, z: number] | undefined; + toLngLatHeight: ( + x: number, + y: number, + z: number, + options?: { useGlobeEllipsoid?: boolean }, + ) => [lng: number, lat: number, height: number] | undefined; flyTo: FlyTo; lookAt: (destination: LookAtDestination, options?: CameraOptions) => void; lookAtLayer: (layerId: string) => void; diff --git a/web/src/beta/lib/core/engines/Cesium/Feature/Marker/index.tsx b/web/src/beta/lib/core/engines/Cesium/Feature/Marker/index.tsx index a29828fb4..29ab2e24a 100644 --- a/web/src/beta/lib/core/engines/Cesium/Feature/Marker/index.tsx +++ b/web/src/beta/lib/core/engines/Cesium/Feature/Marker/index.tsx @@ -68,6 +68,8 @@ export default function Marker({ property, id, isVisible, geometry, layer, featu imageShadowBlur: shadowBlur, imageShadowPositionX: shadowOffsetX, imageShadowPositionY: shadowOffsetY, + eyeOffset, + pixelOffset, heightReference: hr, } = property ?? {}; @@ -98,7 +100,17 @@ export default function Marker({ property, id, isVisible, geometry, layer, featu shadowOffsetY, }); - const pixelOffset = useMemo(() => { + const cartPixelOffset = useMemo( + () => (pixelOffset ? new Cartesian2(...pixelOffset) : undefined), + [pixelOffset], + ); + const cartEyeOffset = useMemo( + () => (eyeOffset ? new Cartesian3(...eyeOffset) : undefined), + [eyeOffset], + ); + + const labelPixelOffset = useMemo(() => { + if (cartPixelOffset) return cartPixelOffset; const padding = 15; const x = (isStyleImage ? imgw : pointSize) / 2 + padding; const y = (isStyleImage ? imgh : pointSize) / 2 + padding; @@ -110,7 +122,7 @@ export default function Marker({ property, id, isVisible, geometry, layer, featu ? y * (labelPos.includes("top") ? -1 : 1) : 0, ); - }, [isStyleImage, imgw, pointSize, imgh, labelPos]); + }, [isStyleImage, imgw, pointSize, imgh, labelPos, cartPixelOffset]); const extrudePointsLineColor = useMemo(() => { return Color.WHITE.withAlpha(0.4); @@ -186,6 +198,8 @@ export default function Marker({ property, id, isVisible, geometry, layer, featu heightReference={heightReference(hr)} distanceDisplayCondition={distanceDisplayCondition} sizeInMeters={imageSizeInMeters} + pixelOffset={cartPixelOffset} + eyeOffset={cartEyeOffset} /> )} {label && ( @@ -204,7 +218,7 @@ export default function Marker({ property, id, isVisible, geometry, layer, featu ? VerticalOrigin.BOTTOM : VerticalOrigin.CENTER } - pixelOffset={pixelOffset} + pixelOffset={labelPixelOffset} fillColor={labelColorCesium} font={toCSSFont(labelTypography, { fontSize: 30 })} text={stringLabelText} diff --git a/web/src/beta/lib/core/engines/Cesium/common.ts b/web/src/beta/lib/core/engines/Cesium/common.ts index 58973a6df..624b07023 100644 --- a/web/src/beta/lib/core/engines/Cesium/common.ts +++ b/web/src/beta/lib/core/engines/Cesium/common.ts @@ -313,6 +313,8 @@ export const lookAt = ( range?: number; /** Field of view expressed in radians */ fov?: number; + /** Radius of bounding sphere */ + radius?: number; }, options?: { /** Seconds */ @@ -337,8 +339,11 @@ export const lookAt = ( : undefined; if (position) { - cesiumCamera.flyToBoundingSphere(new BoundingSphere(position), { - offset: new HeadingPitchRange(camera.heading, camera.pitch, camera.range), + cesiumCamera.flyToBoundingSphere(new BoundingSphere(position, camera.radius), { + offset: + camera.heading !== undefined || camera.pitch !== undefined || camera.range !== undefined + ? new HeadingPitchRange(camera.heading, camera.pitch, camera.range) + : undefined, duration: options?.duration, easingFunction: options?.easing, }); @@ -496,7 +501,8 @@ export const getCamera = (viewer: Viewer | CesiumWidget | undefined): Camera | u const lng = CesiumMath.toDegrees(longitude); const { heading, pitch, roll } = camera; const fov = camera.frustum instanceof PerspectiveFrustum ? camera.frustum.fov : 1; - return { lng, lat, height, heading, pitch, roll, fov }; + const aspectRatio = camera.frustum instanceof PerspectiveFrustum ? camera.frustum.aspectRatio : 1; + return { lng, lat, height, heading, pitch, roll, fov, aspectRatio }; }; export const getClock = (clock: CesiumClock | undefined): Clock | undefined => { diff --git a/web/src/beta/lib/core/engines/Cesium/useEngineRef.ts b/web/src/beta/lib/core/engines/Cesium/useEngineRef.ts index f635172de..bebb82167 100644 --- a/web/src/beta/lib/core/engines/Cesium/useEngineRef.ts +++ b/web/src/beta/lib/core/engines/Cesium/useEngineRef.ts @@ -121,6 +121,35 @@ export default function useEngineRef( if (!viewer || viewer.isDestroyed()) return; return await sampleTerrainHeight(viewer.scene, lng, lat); }, + computeGlobeHeight: (lng, lat, height) => { + const viewer = cesium.current?.cesiumElement; + if (!viewer || viewer.isDestroyed()) return; + return viewer.scene.globe.getHeight(Cesium.Cartographic.fromDegrees(lng, lat, height)); + }, + toXYZ: (lng, lat, height, options) => { + const viewer = cesium.current?.cesiumElement; + if (!viewer || viewer.isDestroyed()) return; + const cartesian = Cesium.Cartesian3.fromDegrees( + lng, + lat, + height, + options?.useGlobeEllipsoid ? viewer.scene.globe.ellipsoid : undefined, + ); + return [cartesian.x, cartesian.y, cartesian.z]; + }, + toLngLatHeight: (x, y, z, options) => { + const viewer = cesium.current?.cesiumElement; + if (!viewer || viewer.isDestroyed()) return; + const cart = Cesium.Cartographic.fromCartesian( + Cesium.Cartesian3.fromElements(x, y, z), + options?.useGlobeEllipsoid ? viewer.scene.globe.ellipsoid : undefined, + ); + return [ + CesiumMath.toDegrees(cart.longitude), + CesiumMath.toDegrees(cart.latitude), + cart.height, + ]; + }, flyTo: (target, options) => { if (target && typeof target === "object") { const viewer = cesium.current?.cesiumElement; diff --git a/web/src/beta/lib/core/mantle/compat/types.ts b/web/src/beta/lib/core/mantle/compat/types.ts index d369449b0..6b4d7fb55 100644 --- a/web/src/beta/lib/core/mantle/compat/types.ts +++ b/web/src/beta/lib/core/mantle/compat/types.ts @@ -77,6 +77,8 @@ export type CameraPosition = { roll: number; /** Field of view expressed in radians */ fov: number; + /** Aspect ratio of frustum */ + aspectRatio?: number; }; export type Typography = { diff --git a/web/src/beta/lib/core/mantle/types/appearance.ts b/web/src/beta/lib/core/mantle/types/appearance.ts index 8223eef2a..ba9012c24 100644 --- a/web/src/beta/lib/core/mantle/types/appearance.ts +++ b/web/src/beta/lib/core/mantle/types/appearance.ts @@ -70,6 +70,8 @@ export type MarkerAppearance = { extrude?: boolean; near?: number; far?: number; + pixelOffset?: [number, number]; + eyeOffset?: [number, number, number]; }; export type PolylineAppearance = { diff --git a/web/src/beta/lib/core/mantle/types/value.ts b/web/src/beta/lib/core/mantle/types/value.ts index 1ce143c91..35885b75f 100644 --- a/web/src/beta/lib/core/mantle/types/value.ts +++ b/web/src/beta/lib/core/mantle/types/value.ts @@ -17,6 +17,7 @@ export type Camera = { pitch: number; roll: number; fov: number; + aspectRatio?: number; }; export type Typography = { diff --git a/web/src/beta/lib/core/types.ts b/web/src/beta/lib/core/types.ts index c519f52f7..6bfad917b 100644 --- a/web/src/beta/lib/core/types.ts +++ b/web/src/beta/lib/core/types.ts @@ -39,4 +39,6 @@ export type LookAtDestination = { range?: number; /** Radian */ fov?: number; + /** Meters */ + radius?: number; };