From 0617096f45d30a85c31263e53686cdc06728b04a Mon Sep 17 00:00:00 2001 From: Pavel Sukhodolskii Date: Tue, 2 Jul 2024 18:23:27 +0400 Subject: [PATCH 1/6] PlanetCamera component --- .storybook/preview.tsx | 1 + .../PlanetCamera/PlanetCamera.stories.tsx | 38 +++++++++++++++ src/camera/PlanetCamera/PlanetCamera.tsx | 46 +++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/camera/PlanetCamera/PlanetCamera.stories.tsx create mode 100644 src/camera/PlanetCamera/PlanetCamera.tsx diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 996a469..4149967 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,6 +1,7 @@ import type {Preview} from "@storybook/react"; const preview: Preview = { + tags: ['autodocs'], parameters: { controls: { matchers: { diff --git a/src/camera/PlanetCamera/PlanetCamera.stories.tsx b/src/camera/PlanetCamera/PlanetCamera.stories.tsx new file mode 100644 index 0000000..11011b8 --- /dev/null +++ b/src/camera/PlanetCamera/PlanetCamera.stories.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import type {Meta, StoryObj} from '@storybook/react'; + +import {Globe, GlobeContextProvider} from "@/Globe"; +import {PlanetCamera, PlanetCameraParams} from "@/camera/PlanetCamera/PlanetCamera"; + +/** + * This story about Billboard component. Billboard is a component that represents a 2d object which always faced to camera. + */ +const meta = { + component: PlanetCamera, + title: 'Components/Camera/PlanetCamera', +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: { + args: PlanetCameraParams + render: (args: PlanetCameraParams) => React.JSX.Element +} = { + + args: { + lon: 0, + lat: 0, + alt: 100000, + lookLon: 10, + lookLat: 10, + lookAlt: 100000, + viewAngle:47 + }, + render: (args: PlanetCameraParams) => + + + + +}; diff --git a/src/camera/PlanetCamera/PlanetCamera.tsx b/src/camera/PlanetCamera/PlanetCamera.tsx new file mode 100644 index 0000000..43b4cdd --- /dev/null +++ b/src/camera/PlanetCamera/PlanetCamera.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import {useEffect} from "react"; +import {LonLat} from "@openglobus/og"; +import {IPlanetCameraParams} from "@openglobus/og/lib/js/camera/PlanetCamera"; +import {useGlobeContext} from "@/Globe"; + +export interface PlanetCameraParams extends IPlanetCameraParams { + lon?: number; + lat?: number; + alt?: number; + yaw?: number; + viewAngle?: number; + lookLon?: number; + lookLat?: number; + lookAlt?: number; +} + +const PlanetCamera: React.FC = ({ + lon, + lat, + alt, + lookLon, + lookLat, + lookAlt, + viewAngle, + ...params + }) => { + const {globe} = useGlobeContext(); + + + useEffect(() => { + if (globe && typeof lon === "number" && typeof lat === "number" && typeof alt === "number") { + globe.planet.flyLonLat(new LonLat(lon, lat, alt), new LonLat(lookLon, lookLat, lookAlt)) + } + }, [lon, lat, alt, lookLon, lookLat, lookAlt, globe]); + + useEffect(() => { + if (globe && typeof viewAngle === "number") { + globe.planet.camera.setViewAngle(viewAngle) + } + }, [viewAngle, globe]); + + return null; +}; + +export {PlanetCamera}; From b797b30e7d122a78a19f0351657e53755acfc8f4 Mon Sep 17 00:00:00 2001 From: Pavel Sukhodolskii Date: Tue, 2 Jul 2024 18:23:56 +0400 Subject: [PATCH 2/6] extend layer base component with handlers --- src/layer/Layer/Layer.stories.tsx | 76 +++++++++++++++++++++++ src/layer/Layer/Layer.tsx | 95 ++++++++++++++++++++++++----- src/layer/Vector/Vector.stories.tsx | 8 ++- src/layer/Vector/Vector.tsx | 10 ++- 4 files changed, 168 insertions(+), 21 deletions(-) create mode 100644 src/layer/Layer/Layer.stories.tsx diff --git a/src/layer/Layer/Layer.stories.tsx b/src/layer/Layer/Layer.stories.tsx new file mode 100644 index 0000000..9922389 --- /dev/null +++ b/src/layer/Layer/Layer.stories.tsx @@ -0,0 +1,76 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import {Billboard, Entity} from "@/entity"; +import {Globe, GlobeContextProvider} from "@/Globe"; +import {action} from '@storybook/addon-actions'; +import React from "react"; +import {Layer, Vector, VectorProps} from "@/layer"; + +const meta = { + title: 'Components/Layer/Layer', + component: Layer, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + + +export const Default: Story = { + parameters: { + actions: {depth: 1, maxDepth: 1}, + }, + argTypes: { + onVisibilityChange: {table: {disable: true}}, + onAdd: {table: {disable: true}}, + onRemove: {table: {disable: true}}, + onMouseMove: {table: {disable: true}}, + onMouseEnter: {table: {disable: true}}, + onMouseLeave: {table: {disable: true}}, + onLclick: {table: {disable: true}}, + onRclick: {table: {disable: true}}, + onMclick: {table: {disable: true}}, + onLdblclick: {table: {disable: true}}, + onRdblclick: {table: {disable: true}}, + onMdblclick: {table: {disable: true}}, + onLup: {table: {disable: true}}, + onRup: {table: {disable: true}}, + onMup: {table: {disable: true}}, + onLdown: {table: {disable: true}}, + onRdown: {table: {disable: true}}, + onMdown: {table: {disable: true}}, + onLhold: {table: {disable: true}}, + onRhold: {table: {disable: true}}, + onMhold: {table: {disable: true}}, + onMouseWheel: {table: {disable: true}}, + onTouchMove: {table: {disable: true}}, + onTouchStart: {table: {disable: true}}, + onTouchEnd: {table: {disable: true}}, + onDoubleTouch: {table: {disable: true}}, + onTouchLeave: {table: {disable: true}}, + onTouchEnter: {table: {disable: true}}, + }, + args: { + name: 'test', + onMouseEnter: action('onMouseEnter', {depth: 1, maxDepth: 1}), + onLclick: action('onLclick', {depth: 1, maxDepth: 1}), + onMouseLeave: action('onMouseLeave', {depth: 1, maxDepth: 1}), + onMouseMove: action('onMouseMove', {depth: 1, maxDepth: 1}), + onLdblclick: action('onLdblclick', {depth: 1, maxDepth: 1}), + // onDraw: action('onDraw', {depth: 1, maxDepth: 1}), + }, + render: (args: VectorProps) => + + + + + + + + +}; + diff --git a/src/layer/Layer/Layer.tsx b/src/layer/Layer/Layer.tsx index 9c10469..34dc4fc 100644 --- a/src/layer/Layer/Layer.tsx +++ b/src/layer/Layer/Layer.tsx @@ -1,23 +1,88 @@ -import {useEffect} from 'react'; -import {useGlobeContext} from '@/Globe'; -import {ILayerParams, Layer as GlobusLayer} from '@openglobus/og'; +import * as React from 'react'; +import {forwardRef, useEffect} from 'react'; +import {ILayerParams, Vector as GlobusVector} from '@openglobus/og'; +import { EventCallback } from '@openglobus/og/lib/js/Events'; +import {useGlobeContext} from "@/Globe"; +export interface LayerProps extends ILayerParams { + name: string + layerRef?: React.MutableRefObject, + children?: React.ReactNode; + onVisibilityChange?: EventCallback; + onAdd?: EventCallback; + onRemove?: EventCallback; + onMouseMove?: EventCallback; + onMouseEnter?: EventCallback; + onMouseLeave?: EventCallback; + onLclick?: EventCallback; + onRclick?: EventCallback; + onMclick?: EventCallback; + onLdblclick?: EventCallback; + onRdblclick?: EventCallback; + onMdblclick?: EventCallback; + onLup?: EventCallback; + onRup?: EventCallback; + onMup?: EventCallback; + onLdown?: EventCallback; + onRdown?: EventCallback; + onMdown?: EventCallback; + onLhold?: EventCallback; + onRhold?: EventCallback; + onMhold?: EventCallback; + onMouseWheel?: EventCallback; + onTouchMove?: EventCallback; + onTouchStart?: EventCallback; + onTouchEnd?: EventCallback; + onDoubleTouch?: EventCallback; + onTouchLeave?: EventCallback; + onTouchEnter?: EventCallback; +} -const Layer = ({props, name}: { props: ILayerParams, name: string }) => { +const Layer: React.FC =({ children, name, layerRef, ...params}) => { const {globe} = useGlobeContext(); + const [refPassed, setRefPassed] = React.useState(false); useEffect(() => { - if (globe) { - const newLayer = new GlobusLayer(name, props); - globe.planet.addLayer(newLayer); - - return () => { - globe.planet.removeLayer(newLayer); - }; + if (refPassed && layerRef && layerRef.current){ + if (params.onVisibilityChange) layerRef.current?.events.on("visibilitychange", params.onVisibilityChange) + if (params.onAdd) layerRef.current?.events.on("add", params.onAdd) + if (params.onRemove) layerRef.current?.events.on("remove", params.onRemove) + if (params.onMouseMove) layerRef.current?.events.on("mousemove", params.onMouseMove) + if (params.onMouseEnter) layerRef.current?.events.on("mouseenter", params.onMouseEnter) + if (params.onMouseLeave) layerRef.current?.events.on("mouseleave", params.onMouseLeave) + if (params.onLclick) layerRef.current?.events.on("lclick", params.onLclick) + if (params.onRclick) layerRef.current?.events.on("rclick", params.onRclick) + if (params.onMclick) layerRef.current?.events.on("mclick", params.onMclick) + if (params.onLdblclick) layerRef.current?.events.on("ldblclick", params.onLdblclick) + if (params.onRdblclick) layerRef.current?.events.on("rdblclick", params.onRdblclick) + if (params.onMdblclick) layerRef.current?.events.on("mdblclick", params.onMdblclick) + if (params.onLup) layerRef.current?.events.on("lup", params.onLup) + if (params.onRup) layerRef.current?.events.on("rup", params.onRup) + if (params.onMup) layerRef.current?.events.on("mup", params.onMup) + if (params.onLdown) layerRef.current?.events.on("ldown", params.onLdown) + if (params.onRdown) layerRef.current?.events.on("rdown", params.onRdown) + if (params.onMdown) layerRef.current?.events.on("mdown", params.onMdown) + if (params.onLhold) layerRef.current?.events.on("lhold", params.onLhold) + if (params.onRhold) layerRef.current?.events.on("rhold", params.onRhold) + if (params.onMhold) layerRef.current?.events.on("mhold", params.onMhold) + if (params.onMouseWheel) layerRef.current?.events.on("mousewheel", params.onMouseWheel) + if (params.onTouchMove) layerRef.current?.events.on("touchmove", params.onTouchMove) + if (params.onTouchStart) layerRef.current?.events.on("touchstart", params.onTouchStart) + if (params.onTouchEnd) layerRef.current?.events.on("touchend", params.onTouchEnd) + if (params.onDoubleTouch) layerRef.current?.events.on("doubletouch", params.onDoubleTouch) } - }, [globe]); - - return null; + return () => { + if (params.onLclick) layerRef?.current?.events.off('lclick', params.onLclick) + setRefPassed(false) + } + }, [refPassed]); + if (layerRef?.current && !refPassed) { + setRefPassed(true) + } + return ( + <> + {children} + + ); }; export {Layer}; - diff --git a/src/layer/Vector/Vector.stories.tsx b/src/layer/Vector/Vector.stories.tsx index 0a8515a..6ca7d7d 100644 --- a/src/layer/Vector/Vector.stories.tsx +++ b/src/layer/Vector/Vector.stories.tsx @@ -1,6 +1,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import {Vector, VectorProps} from './Vector'; +import {Default as LayerDefault} from '../Layer/Layer.stories'; import {Billboard, Entity} from "@/entity"; import {Globe, GlobeContextProvider} from "@/Globe"; import {action} from '@storybook/addon-actions'; @@ -20,10 +21,11 @@ export const Default: Story = { parameters: { actions: {depth: 1, maxDepth: 1}, }, + argTypes:{ + ...LayerDefault.argTypes + }, args: { - name: 'test', - onMouseEnter: action('onMouseEnter', {depth: 1, maxDepth: 1}), - // onDraw: action('onDraw', {depth: 1, maxDepth: 1}), + name: 'test' }, render: (args: VectorProps) => diff --git a/src/layer/Vector/Vector.tsx b/src/layer/Vector/Vector.tsx index 3881f73..4d3588d 100644 --- a/src/layer/Vector/Vector.tsx +++ b/src/layer/Vector/Vector.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import {createContext, useCallback, useEffect, useRef, useState} from "react"; -import {useGlobeContext} from '../../index'; +import {Layer, LayerProps, useGlobeContext} from '../../index'; import type {Billboard, Entity, Geometry, GeoObject, Label, Polyline, Strip} from '@openglobus/og'; import {Vector as GlobusVector} from '@openglobus/og'; import {IVectorParams} from '@openglobus/og/lib/js/layer/Vector'; @@ -54,13 +54,15 @@ const VectorContext = createContext<{ type EntityElement = React.ReactElement<{ type: typeof Entity }>; -export interface VectorProps extends IVectorParams { +export interface VectorPropsBase extends IVectorParams { children?: EntityElement | EntityElement[] name: string onMouseEnter?: EventCallback onDraw?: EventCallback } +export type VectorProps = VectorPropsBase & LayerProps; + const Vector: React.FC = ({visibility, children, name, ...rest}) => { const {globe} = useGlobeContext(); const vectorRef = useRef(null); @@ -179,7 +181,9 @@ const Vector: React.FC = ({visibility, children, name, ...rest}) => addStrip, removeStrip }}> - {children} + + {children} + ); }; From 8f8b5574a80df88211eec402a1725b4e9685cd4b Mon Sep 17 00:00:00 2001 From: Pavel Sukhodolski Date: Fri, 12 Jul 2024 02:03:18 +0400 Subject: [PATCH 3/6] pass lon lat alt into entity when create --- src/entity/Entity/Entity.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/Entity/Entity.tsx b/src/entity/Entity/Entity.tsx index bcac3b8..8c42d8f 100644 --- a/src/entity/Entity/Entity.tsx +++ b/src/entity/Entity/Entity.tsx @@ -81,7 +81,7 @@ const Entity: React.FC = ({visibility, lon, lat, alt, lonlat, name useEffect(() => { if (globe) { entityRef.current = new GlobusEntity({ - lonlat, name, ...rest + lonlat: lonlat? lonlat : new LonLat(lon,lat,alt), name, ...rest }); addEntity(entityRef.current); From 3c426f0f8483cc19e62b91433c878cf19ba25c90 Mon Sep 17 00:00:00 2001 From: Pavel Sukhodolski Date: Mon, 22 Jul 2024 12:07:23 +0400 Subject: [PATCH 4/6] add layer to xyz --- src/Globe/Globe.tsx | 9 +++++++-- src/layer/Layer/Layer.tsx | 23 ++++++++++++++--------- src/layer/Vector/Vector.stories.tsx | 3 ++- src/layer/XYZ/XYZ.stories.tsx | 2 ++ src/layer/XYZ/XYZ.tsx | 6 +++++- vite.config.ts | 2 +- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Globe/Globe.tsx b/src/Globe/Globe.tsx index d3ee878..310e854 100644 --- a/src/Globe/Globe.tsx +++ b/src/Globe/Globe.tsx @@ -7,7 +7,8 @@ import {IGlobeParams} from "@openglobus/og/lib/js/Globe"; import "@openglobus/og/css/og.css"; import {Layer, Vector} from "@/layer"; -type LayerChildren = React.ReactElement<{ type: typeof Layer|typeof Vector}>; +type LayerChildren = React.ReactElement<{ type: typeof Layer | typeof Vector }>; + export interface GlobusProps extends IGlobeParams { children?: LayerChildren | LayerChildren[] atmosphereEnabled?: boolean, @@ -68,7 +69,11 @@ const Globe: React.FC = ({children, onDraw, ...rest}) => { nightTextureCoefficient: 2.7, urlRewrite: function (s: any, u: string) { // @ts-ignore - return utils.stringTemplate(u, {s: this._getSubdomain(), quad: toQuadKey(s.tileX, s.tileY, s.tileZoom)});}, + return utils.stringTemplate(u, { + s: this._getSubdomain(), + quad: toQuadKey(s.tileX, s.tileY, s.tileZoom) + }); + }, }); gRef.current = new GlobusGlobe({ diff --git a/src/layer/Layer/Layer.tsx b/src/layer/Layer/Layer.tsx index 34dc4fc..376fd0f 100644 --- a/src/layer/Layer/Layer.tsx +++ b/src/layer/Layer/Layer.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; -import {forwardRef, useEffect} from 'react'; -import {ILayerParams, Vector as GlobusVector} from '@openglobus/og'; -import { EventCallback } from '@openglobus/og/lib/js/Events'; -import {useGlobeContext} from "@/Globe"; +import {useEffect} from 'react'; +import {ILayerParams, Layer as GlobusLayer} from '@openglobus/og'; +import {EventCallback} from '@openglobus/og/lib/js/Events'; + export interface LayerProps extends ILayerParams { name: string - layerRef?: React.MutableRefObject, + layerRef?: React.MutableRefObject, children?: React.ReactNode; onVisibilityChange?: EventCallback; onAdd?: EventCallback; @@ -37,12 +37,17 @@ export interface LayerProps extends ILayerParams { onTouchEnter?: EventCallback; } -const Layer: React.FC =({ children, name, layerRef, ...params}) => { - const {globe} = useGlobeContext(); +const Layer: React.FC = ({opacity, children, name, layerRef, ...params}) => { const [refPassed, setRefPassed] = React.useState(false); - useEffect(() => { - if (refPassed && layerRef && layerRef.current){ + if (layerRef) { + if (layerRef.current && typeof opacity === 'number' && refPassed) { + layerRef.current.opacity = opacity; + } + } + }, [opacity]); + useEffect(() => { + if (refPassed && layerRef && layerRef.current) { if (params.onVisibilityChange) layerRef.current?.events.on("visibilitychange", params.onVisibilityChange) if (params.onAdd) layerRef.current?.events.on("add", params.onAdd) if (params.onRemove) layerRef.current?.events.on("remove", params.onRemove) diff --git a/src/layer/Vector/Vector.stories.tsx b/src/layer/Vector/Vector.stories.tsx index 6ca7d7d..8207a4c 100644 --- a/src/layer/Vector/Vector.stories.tsx +++ b/src/layer/Vector/Vector.stories.tsx @@ -25,7 +25,8 @@ export const Default: Story = { ...LayerDefault.argTypes }, args: { - name: 'test' + name: 'test', + opacity: 1 }, render: (args: VectorProps) => diff --git a/src/layer/XYZ/XYZ.stories.tsx b/src/layer/XYZ/XYZ.stories.tsx index 35202c4..a7f88ed 100644 --- a/src/layer/XYZ/XYZ.stories.tsx +++ b/src/layer/XYZ/XYZ.stories.tsx @@ -31,6 +31,7 @@ export const OpenStreetMap: Story = { args: { name: 'osm', + opacity: 1, url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' }, render: (args: XYZProps) => @@ -43,6 +44,7 @@ export const OpenStreetMap: Story = { export const Satellites: Story = { args: { name: 'sat', + opacity: 1, url: 'https://ecn.{s}.tiles.virtualearth.net/tiles/a{quad}.jpeg?n=z&g=7146', subdomains: ['t0', 't1', 't2', 't3'], urlRewrite: function (s: any, u: string) { diff --git a/src/layer/XYZ/XYZ.tsx b/src/layer/XYZ/XYZ.tsx index bbca6c9..9de48d8 100644 --- a/src/layer/XYZ/XYZ.tsx +++ b/src/layer/XYZ/XYZ.tsx @@ -1,6 +1,8 @@ +import * as React from 'react'; import {useEffect, useRef} from 'react'; import {useGlobeContext} from '@/Globe'; import {IXYZParams, XYZ as GlobusXYZ} from '@openglobus/og'; +import {Layer} from "@/layer"; export interface XYZProps extends IXYZParams { name: string; @@ -28,7 +30,9 @@ const XYZ: React.FC = ({name, ...rest}) => { } }, [globe]); - return null; + return + + ; }; export {XYZ}; \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 077ee80..7b52478 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,7 +7,7 @@ import {fileURLToPath, URL} from "url"; export default defineConfig({ plugins: [dts({ include: ['src/**/*.ts', 'src/**/*.tsx'], - exclude: ['node_modules', 'dist'], + exclude: ['node_modules','src/**/*.stories.ts', 'dist'], outDir: 'dist/types', })], resolve: { From 6f0d6708401fab43b838f049f100335aa075edb1 Mon Sep 17 00:00:00 2001 From: Pavel Sukhodolski Date: Mon, 22 Jul 2024 13:05:54 +0400 Subject: [PATCH 5/6] add layer to xyz --- src/Globe/Globe.tsx | 5 +---- vite.config.ts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Globe/Globe.tsx b/src/Globe/Globe.tsx index 310e854..54534c1 100644 --- a/src/Globe/Globe.tsx +++ b/src/Globe/Globe.tsx @@ -69,10 +69,7 @@ const Globe: React.FC = ({children, onDraw, ...rest}) => { nightTextureCoefficient: 2.7, urlRewrite: function (s: any, u: string) { // @ts-ignore - return utils.stringTemplate(u, { - s: this._getSubdomain(), - quad: toQuadKey(s.tileX, s.tileY, s.tileZoom) - }); + return utils.stringTemplate(u, {s: this._getSubdomain(), quad: toQuadKey(s.tileX, s.tileY, s.tileZoom)}); }, }); diff --git a/vite.config.ts b/vite.config.ts index 7b52478..141f685 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,7 +7,7 @@ import {fileURLToPath, URL} from "url"; export default defineConfig({ plugins: [dts({ include: ['src/**/*.ts', 'src/**/*.tsx'], - exclude: ['node_modules','src/**/*.stories.ts', 'dist'], + exclude: ['node_modules','src/**/*.stories.tsx', 'dist'], outDir: 'dist/types', })], resolve: { From db0aac6599f92a29b3647a22c49c0d66e0b4ffb3 Mon Sep 17 00:00:00 2001 From: Pavel Sukhodolski Date: Mon, 22 Jul 2024 17:17:46 +0400 Subject: [PATCH 6/6] bump versions --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 98f44f5..3f643e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openglobus/openglobus-react", - "version": "0.1.0", + "version": "0.1.1", "description": "Openglobus React Components", "main": "./dist/index.umd.cjs", "private": false, @@ -28,7 +28,7 @@ "author": "Pavel Sukhodolski", "license": "MIT", "dependencies": { - "@openglobus/og": "^0.20.2", + "@openglobus/og": "^0.20.3", "react": "^18.2.0" }, "devDependencies": {