Skip to content

Commit

Permalink
feat: support select feature on reearth/core (#445)
Browse files Browse the repository at this point in the history
* feat: support select feature

* refactor: use foreach
  • Loading branch information
keiya01 committed Feb 10, 2023
1 parent b042949 commit 3174b17
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 45 deletions.
9 changes: 7 additions & 2 deletions src/core/Crust/Plugins/api.ts
Expand Up @@ -291,6 +291,7 @@ export function commonReearth({
pluginInstances,
viewport,
selectedLayer,
selectedFeature,
layerSelectionReason,
layerOverriddenProperties,
selectLayer,
Expand Down Expand Up @@ -330,6 +331,7 @@ export function commonReearth({
clock: () => GlobalThis["reearth"]["clock"];
pluginInstances: () => PluginInstances;
selectedLayer: () => GlobalThis["reearth"]["layers"]["selected"];
selectedFeature: () => GlobalThis["reearth"]["layers"]["selectedFeature"];
layerSelectionReason: () => GlobalThis["reearth"]["layers"]["selectionReason"];
layerOverriddenProperties?: () => GlobalThis["reearth"]["layers"]["overriddenProperties"];
selectLayer: LayersRef["select"];
Expand Down Expand Up @@ -445,8 +447,8 @@ export function commonReearth({
},
get select() {
// For compat
return (layerId: string | undefined, reason?: LayerSelectionReason | undefined) =>
selectLayer?.(layerId, undefined, reason);
return (id: string | undefined, reason?: LayerSelectionReason | undefined) =>
selectLayer?.(id, id, reason);
},
get show() {
return showLayer;
Expand Down Expand Up @@ -478,6 +480,9 @@ export function commonReearth({
get selected() {
return selectedLayer();
},
get selectedFeature() {
return selectedFeature();
},
get findById() {
return layers()?.findById;
},
Expand Down
4 changes: 4 additions & 0 deletions src/core/Crust/Plugins/hooks.ts
Expand Up @@ -32,6 +32,7 @@ export default function ({
tags,
viewport,
selectedLayer,
selectedFeature,
layerSelectionReason,
alignSystem,
floatingWidgets,
Expand Down Expand Up @@ -73,6 +74,7 @@ export default function ({
const getPluginInstances = useGet(pluginInstances);
const getViewport = useGet(viewport as Viewport);
const getSelectedLayer = useGet(selectedLayer);
const getSelectedFeature = useGet(selectedFeature);
const getLayerSelectionReason = useGet(layerSelectionReason);
const overrideScenePropertyCommon = useCallback(
(property: any) => {
Expand Down Expand Up @@ -277,6 +279,7 @@ export default function ({
pluginInstances: getPluginInstances,
viewport: getViewport,
selectedLayer: getSelectedLayer,
selectedFeature: getSelectedFeature,
layerSelectionReason: getLayerSelectionReason,
layerOverriddenProperties,
showLayer,
Expand Down Expand Up @@ -323,6 +326,7 @@ export default function ({
getPluginInstances,
getViewport,
getSelectedLayer,
getSelectedFeature,
getLayerSelectionReason,
overrideScenePropertyCommon,
lookAt,
Expand Down
3 changes: 3 additions & 0 deletions src/core/Crust/Plugins/plugin_types.ts
@@ -1,5 +1,6 @@
import type {
ComputedLayer,
ComputedFeature,
LatLngHeight,
Rect,
CameraPosition,
Expand Down Expand Up @@ -68,6 +69,7 @@ export type Reearth = {
| "replace"
| "deleteLayer"
| "selectedLayer"
| "selectedFeature"
| "overriddenLayers"
> & {
readonly layersInViewport?: () => LazyLayer[] | undefined;
Expand All @@ -84,6 +86,7 @@ export type Reearth = {
layers?: LazyLayer[];
isLayer?: boolean;
selected?: ComputedLayer;
selectedFeature?: ComputedFeature;
}
>;
readonly layer?: LazyLayer;
Expand Down
3 changes: 2 additions & 1 deletion src/core/Crust/Plugins/types.ts
@@ -1,6 +1,6 @@
import type { PropsWithChildren, RefObject } from "react";

import type { Camera, Tag } from "@reearth/core/mantle";
import type { Camera, ComputedFeature, Tag } from "@reearth/core/mantle";
import type { ComputedLayer, LayerEditEvent, LayerSelectionReason } from "@reearth/core/Map";
import type { Viewport } from "@reearth/core/Visualizer";

Expand All @@ -18,6 +18,7 @@ export type Props = PropsWithChildren<{
inEditor?: boolean;
tags?: Tag[];
selectedLayer?: ComputedLayer;
selectedFeature?: ComputedFeature;
layerSelectionReason?: LayerSelectionReason;
viewport?: Viewport;
alignSystem?: WidgetAlignSystem;
Expand Down
2 changes: 2 additions & 0 deletions src/core/Crust/index.tsx
Expand Up @@ -121,6 +121,7 @@ export default function Crust({
selectedLayerId,
selectedReason,
selectedComputedLayer,
selectedComputedFeature,
widgetAlignSystem,
widgetAlignSystemEditing,
widgetLayoutConstraint,
Expand Down Expand Up @@ -165,6 +166,7 @@ export default function Crust({
inEditor={inEditor}
tags={tags}
selectedLayer={selectedComputedLayer}
selectedFeature={selectedComputedFeature}
layerSelectionReason={selectedReason}
viewport={viewport}
alignSystem={widgetAlignSystem}
Expand Down
30 changes: 14 additions & 16 deletions src/core/Map/Layers/hooks.ts
Expand Up @@ -676,22 +676,20 @@ function useSelection({

useEffect(() => {
const actualSelectedLayer = selectedLayerForRef();
if (actualSelectedLayer) {
onLayerSelect?.(
actualSelectedLayer.id,
selectedLayerId?.featureId,
() =>
new Promise(resolve => {
// Wait until computed feature is ready
queueMicrotask(() => {
resolve(actualSelectedLayer.computed);
});
}),
selectedReason,
);
} else {
onLayerSelect?.(undefined, undefined, undefined, undefined);
}
onLayerSelect?.(
actualSelectedLayer?.id,
selectedLayerId?.featureId,
actualSelectedLayer
? () =>
new Promise(resolve => {
// Wait until computed feature is ready
queueMicrotask(() => {
resolve(actualSelectedLayer?.computed);
});
})
: undefined,
selectedReason,
);
}, [onLayerSelect, selectedLayerId, selectedReason, selectedLayerForRef]);

useEffect(() => {
Expand Down
20 changes: 1 addition & 19 deletions src/core/engines/Cesium/Feature/Tileset/hooks.ts
Expand Up @@ -12,15 +12,14 @@ import {
Matrix3,
Cesium3DTileset,
Cesium3DTile,
Cesium3DTileContent,
Cesium3DTileFeature,
} from "cesium";
import { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { CesiumComponentRef, useCesium } from "resium";

import type { ComputedFeature, ComputedLayer, Feature, EvalFeature, SceneProperty } from "../../..";
import { layerIdField, sampleTerrainHeightFromCartesian } from "../../common";
import { translationWithClamping } from "../../utils";
import { lookupFeatures, translationWithClamping } from "../../utils";
import { attachTag, extractSimpleLayerData, toColor } from "../utils";

import { Property } from ".";
Expand Down Expand Up @@ -110,23 +109,6 @@ const useFeature = ({
);

useEffect(() => {
function lookupFeatures(
c: Cesium3DTileContent,
cb: (feature: Cesium3DTileFeature, content: Cesium3DTileContent) => void,
) {
if (!c) return [];
const length = c.featuresLength;
for (let i = 0; i < length; i++) {
const f = c.getFeature(i);
if (!f) {
continue;
}
cb(f, c);
}
c.innerContents?.flatMap(c => lookupFeatures(c, cb));
return;
}

const currentTiles: Map<Cesium3DTile, string> = new Map();

tileset.current?.tileLoad.addEventListener((t: Cesium3DTile) => {
Expand Down
30 changes: 26 additions & 4 deletions src/core/engines/Cesium/hooks.ts
@@ -1,4 +1,4 @@
import { Color, Entity, Cesium3DTileFeature, Cartesian3, Ion } from "cesium";
import { Color, Entity, Cesium3DTileFeature, Cartesian3, Ion, Cesium3DTileset } from "cesium";
import type { Viewer as CesiumViewer } from "cesium";
import CesiumDnD, { Context } from "cesium-dnd";
import { isEqual } from "lodash-es";
Expand Down Expand Up @@ -148,19 +148,41 @@ export default ({
}
}, [camera, engineAPI]);

const prevSelectedEntity = useRef<Entity | Cesium3DTileset | Cesium3DTileFeature>();
// manage layer selection
useEffect(() => {
const viewer = cesium.current?.cesiumElement;
if (!viewer || viewer.isDestroyed()) return;

const entity = findEntity(viewer, selectedLayerId?.layerId, selectedLayerId?.featureId);
if (viewer.selectedEntity === entity) return;
if (prevSelectedEntity.current === entity) return;
prevSelectedEntity.current = entity;

const tag = getTag(entity);
if (tag?.unselectable) return;

viewer.selectedEntity = entity;
}, [cesium, selectedLayerId]);
if (entity && entity instanceof Cesium3DTileFeature) {
const tag = getTag(entity);
if (tag) {
onLayerSelect?.(tag.layerId, String(tag.featureId), {
overriddenInfobox: {
title: entity.getProperty("name"),
content: tileProperties(entity),
},
});
}
return;
}

if (entity) {
// Sometimes only featureId is specified, so we need to sync entity tag.
onLayerSelect?.(tag?.layerId, tag?.featureId);
}

if (!entity || entity instanceof Entity) {
viewer.selectedEntity = entity;
}
}, [cesium, selectedLayerId, onLayerSelect]);

const handleMouseEvent = useCallback(
(type: keyof MouseEvents, e: CesiumMovementEvent, target: RootEventTarget) => {
Expand Down
4 changes: 2 additions & 2 deletions src/core/engines/Cesium/useEngineRef.ts
Expand Up @@ -87,11 +87,11 @@ export default function useEngineRef(

const layerOrFeatureId = target;
const entityFromFeatureId = findEntity(viewer, undefined, layerOrFeatureId);
if (entityFromFeatureId) {
if (entityFromFeatureId && !(entityFromFeatureId instanceof Cesium.Cesium3DTileFeature)) {
viewer.flyTo(entityFromFeatureId, options);
} else {
const entityFromLayerId = findEntity(viewer, layerOrFeatureId);
if (entityFromLayerId) {
if (entityFromLayerId && !(entityFromLayerId instanceof Cesium.Cesium3DTileFeature)) {
viewer.flyTo(entityFromLayerId, options);
}
}
Expand Down
73 changes: 72 additions & 1 deletion src/core/engines/Cesium/utils.ts
Expand Up @@ -5,6 +5,10 @@ import {
TranslationRotationScale,
Cartographic,
Entity,
Cesium3DTile,
Cesium3DTileset,
Cesium3DTileContent,
Cesium3DTileFeature,
} from "cesium";

import { getTag } from "./Feature";
Expand Down Expand Up @@ -39,11 +43,52 @@ export const translationWithClamping = (
}
};

export function lookupFeatures(
c: Cesium3DTileContent,
cb: (feature: Cesium3DTileFeature, content: Cesium3DTileContent) => void,
) {
if (!c) return;
const length = c.featuresLength;
for (let i = 0; i < length; i++) {
const f = c.getFeature(i);
if (!f) {
continue;
}
cb(f, c);
}
c.innerContents?.forEach(c => lookupFeatures(c, cb));
return;
}

const findFeatureFrom3DTile = (
tile: Cesium3DTile,
featureId?: string,
): Cesium3DTileFeature | void => {
let target: Cesium3DTileFeature | undefined = undefined;
lookupFeatures(tile.content, f => {
const tag = getTag(f);
if (tag?.featureId === featureId) {
target = f;
}
});

if (target) {
return target;
}

for (const child of tile.children) {
const t = findFeatureFrom3DTile(child, featureId);
if (t) {
return t;
}
}
};

export function findEntity(
viewer: CesiumViewer,
layerId?: string,
featureId?: string,
): Entity | undefined {
): Entity | Cesium3DTileset | Cesium3DTileFeature | undefined {
const id = featureId ?? layerId;
const keyName = featureId ? "featureId" : "layerId";
if (!id) return;
Expand All @@ -64,5 +109,31 @@ export function findEntity(
}
}

// Find Cesium3DTileFeature
for (let i = 0; i < viewer.scene.primitives.length; i++) {
const prim = viewer.scene.primitives.get(i);
if (!(prim instanceof Cesium3DTileset)) {
continue;
}

const target = findFeatureFrom3DTile(prim.root, featureId);
if (target) {
return target;
}
}

// Find Cesium3DTileset
for (let i = 0; i < viewer.scene.primitives.length; i++) {
const prim = viewer.scene.primitives.get(i);
if (!(prim instanceof Cesium3DTileset)) {
continue;
}

const tag = getTag(prim);
if (tag?.layerId === layerId) {
return prim;
}
}

return;
}

0 comments on commit 3174b17

Please sign in to comment.