Skip to content

Commit

Permalink
feat: add support for node data fetching in React Components
Browse files Browse the repository at this point in the history
  • Loading branch information
haakonflatval-cognite committed Jul 27, 2023
1 parent f24bc83 commit 3c737c8
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { useRef, type ReactElement, useContext, useState, useEffect } from 'reac
import {
type NodeAppearance,
type Cognite3DViewer,
type PointCloudAppearance
type PointCloudAppearance,
type PointerEventData,
} from '@cognite/reveal';
import { ModelsLoadingStateContext } from './ModelsLoadingContext';
import { CadModelContainer, type CadModelStyling } from '../CadModelContainer/CadModelContainer';
Expand All @@ -19,11 +20,14 @@ import {
type AddReveal3DModelOptions,
type AddImageCollection360Options,
type TypedReveal3DModel,
type AddResourceOptions
type AddResourceOptions,
type NodeDataResult
} from './types';
import { type CogniteExternalId } from '@cognite/sdk';
import { type FdmAssetMappingsConfig } from '../../hooks/types';
import { useCalculateModelsStyling } from '../../hooks/useCalculateModelsStyling';
import { queryMappedData } from './queryMappedData';
import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider';

export type FdmAssetStylingGroup = {
fdmAssetExternalIds: CogniteExternalId[];
Expand All @@ -35,24 +39,28 @@ export type Reveal3DResourcesStyling = {
groups?: FdmAssetStylingGroup[];
};

export type Reveal3DResourcesProps = {
export type Reveal3DResourcesProps<NodeType = any> = {
resources: AddResourceOptions[];
fdmAssetMappingConfig?: FdmAssetMappingsConfig;
fdmAssetMappingConfig: FdmAssetMappingsConfig;
styling?: Reveal3DResourcesStyling;
onNodeClick?: (node: NodeDataResult<NodeType>) => void;
};

export const Reveal3DResources = ({
export const Reveal3DResources = <NodeType,>({
resources,
styling,
fdmAssetMappingConfig
}: Reveal3DResourcesProps): ReactElement => {
fdmAssetMappingConfig,
onNodeClick
}: Reveal3DResourcesProps<NodeType>): ReactElement => {
const [reveal3DModels, setReveal3DModels] = useState<TypedReveal3DModel[]>([]);
const [reveal3DModelsStyling, setReveal3DModelsStyling] = useState<
Array<PointCloudModelStyling | CadModelStyling>
>([]);

const { setModelsAdded } = useContext(ModelsLoadingStateContext);
const viewer = useReveal();
const fdmSdk = useFdmSdk();
const client = useSDK();
const numModelsLoaded = useRef(0);

useEffect(() => {
Expand All @@ -65,6 +73,19 @@ export const Reveal3DResources = ({
setReveal3DModelsStyling(modelsStyling);
}, [modelsStyling]);

useEffect(() => {
const callback = async (event: PointerEventData) => {
const data = await queryMappedData<NodeType>(viewer, client, fdmSdk, fdmAssetMappingConfig, event);
if (onNodeClick !== undefined && data !== undefined) {
onNodeClick?.(data);
}
}

viewer.on('click', callback);

return () => viewer.off('click', callback);
}, [onNodeClick]);

const image360CollectionAddOptions = resources.filter(
(resource): resource is AddImageCollection360Options =>
(resource as AddImageCollection360Options).siteId !== undefined
Expand Down
136 changes: 136 additions & 0 deletions react-components/src/components/Reveal3DResources/queryMappedData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*!
* Copyright 2023 Cognite AS
*/

import { Cognite3DViewer, PointerEventData, CadIntersection } from '@cognite/reveal';
import { CogniteClient } from '@cognite/sdk';
import { FdmSDK } from '../../utilities/FdmSDK';
import { FdmAssetMappingsConfig } from '../../hooks/types';
import { NodeDataResult } from './types';

export async function queryMappedData<NodeType>(
viewer: Cognite3DViewer,
cdfClient: CogniteClient,
fdmClient: FdmSDK,
fdmConfig: FdmAssetMappingsConfig,
clickEvent: PointerEventData
): Promise<NodeDataResult<NodeType> | undefined> {
const intersection = await viewer.getIntersectionFromPixel(
clickEvent.offsetX,
clickEvent.offsetY
);

if (intersection === null || intersection.type !== 'cad') {
return;
}

const cadIntersection = intersection as CadIntersection;
const model = cadIntersection.model;

const nodeId = await cadIntersection.model.mapTreeIndexToNodeId(
cadIntersection.treeIndex
);

const ancestorNodes = await cdfClient.revisions3D.list3DNodeAncestors(
model.modelId,
model.revisionId,
nodeId
);

const ancestorIds = ancestorNodes.items.map((n) => n.id);

const filter = {
and: [
{
equals: {
property: ['edge', 'endNode'],
value: {
space: fdmConfig.global3dSpace,
externalId: 'model_3d_' + model.modelId,
},
},
},
{
equals: {
property: [
fdmConfig.source.space,
`${fdmConfig.source.externalId}/${fdmConfig.source.version}`,
'revisionId',
],
value: model.revisionId,
},
},
{
in: {
property: [
fdmConfig.source.space,
`${fdmConfig.source.externalId}/${fdmConfig.source.version}`,
'nodeId',
],
values: ancestorIds,
},
},
],
};

let mappings = await fdmClient.filterInstances(
filter,
'edge',
fdmConfig.source
);

while (mappings.nextCursor) {
const nextMappings = await fdmClient.filterInstances(
filter,
'edge',
fdmConfig.source,
mappings.nextCursor
);

mappings = {
edges: [...mappings.edges, ...nextMappings.edges],
nextCursor: nextMappings.nextCursor,
};
}

if (mappings.edges.length === 0) {
return;
}

const dataNode = mappings.edges[0].startNode;

const inspectionResult = await fdmClient.inspectInstances({
inspectionOperations: { involvedViewsAndContainers: {} },
items: [
{
instanceType: 'node',
externalId: dataNode.externalId,
space: dataNode.space,
},
],
});

const dataView =
inspectionResult.items[0].inspectionResults.involvedViewsAndContainers
.views[0];

const dataQueryResult = await fdmClient.filterInstances(
{
and: [
{ equals: { property: ['node', 'space'], value: dataNode.space } },
{
equals: {
property: ['node', 'externalId'],
value: dataNode.externalId,
},
},
],
},
'node',
dataView
);

const nodeData = dataQueryResult.edges[0].properties[dataView.space][`${dataView.externalId}/${dataView.version}`];

return { data: nodeData as NodeType, view: dataView };
}
12 changes: 12 additions & 0 deletions react-components/src/components/Reveal3DResources/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@ export type AddResourceOptions = AddReveal3DModelOptions | AddImageCollection360

export type AddReveal3DModelOptions = AddModelOptions & { transform?: Matrix4 };
export type TypedReveal3DModel = AddReveal3DModelOptions & { type: SupportedModelTypes | '' };

export type ViewInfo = {
type: 'view',
space: string,
externalId: string,
version: string,
};

export type NodeDataResult<NodeType> = {
data: NodeType,
view: ViewInfo
};
5 changes: 4 additions & 1 deletion react-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export { CameraController } from './components/CameraController/CameraController
export type {
AddImageCollection360Options,
AddResourceOptions,
AddReveal3DModelOptions
AddReveal3DModelOptions,
NodeDataResult,
ViewInfo
} from './components/Reveal3DResources/types';
export { RevealToolbar } from './components/RevealToolbar/RevealToolbar';
export { LayersButton } from './components/RevealToolbar/LayersButton';
Expand All @@ -36,3 +38,4 @@ export { FitModelsButton } from './components/RevealToolbar/FitModelsButton';
export { useFdmAssetMappings } from './hooks/useFdmAssetMappings';
export { type FdmAssetMappingsConfig } from './hooks/types';
export { use3DModelName } from './hooks/use3DModelName';
export { useReveal } from './components/RevealContainer/RevealContext';
45 changes: 45 additions & 0 deletions react-components/src/utilities/FdmSDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,49 @@ export type EdgeItem<PropertiesType> = {
properties: PropertiesType;
};

export type InspectFilter = {
inspectionOperations: { involvedViewsAndContainers: {} };
items: { instanceType: InstanceType; externalId: string; space: string }[];
};

export type InspectResult = {
involvedViewsAndContainers: {
containers: {
type: 'container';
space: string;
externalId: string;
}[];
views: {
type: 'view';
space: string;
externalId: string;
version: string;
}[];
};
};

export type InspectResultList = {
items: {
instanceType: InstanceType;
externalId: string;
space: string;
inspectionResults: InspectResult;
}[];
};

export class FdmSDK {
private readonly _sdk: CogniteClient;
private readonly _byIdsEndpoint: string;
private readonly _listEndpoint: string;
private readonly _inspectEndpoint: string;

constructor(sdk: CogniteClient) {
const baseUrl = sdk.getBaseUrl();
const project = sdk.project;

this._listEndpoint = `${baseUrl}/api/v1/projects/${project}/models/instances/list`;
this._byIdsEndpoint = `${baseUrl}/api/v1/projects/${project}/models/instances/byids`;
this._inspectEndpoint = `${baseUrl}/api/v1/projects/${project}/models/instances/inspect`;

this._sdk = sdk;
}
Expand Down Expand Up @@ -64,4 +96,17 @@ export class FdmSDK {
}
throw new Error(`Failed to fetch instances. Status: ${result.status}`);
}

public async inspectInstances(
inspectFilter: InspectFilter
): Promise<InspectResultList> {
const data: any = inspectFilter;
const result = await this._sdk.post(this._inspectEndpoint, { data });

if (result.status === 200) {
return result.data as InspectResultList;
}

throw new Error(`Failed to fetch instances. Status: ${result.status}`);
}
}

0 comments on commit 3c737c8

Please sign in to comment.