Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(react-components): create and delete observations #4658

Merged
merged 20 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b61bb95
feat(react-components): create and delete observations
haakonflatval-cognite Jul 10, 2024
4641907
chore: lint fix
haakonflatval-cognite Jul 10, 2024
142fbc4
chore: Make source argument required
haakonflatval-cognite Jul 11, 2024
96ca80c
chore: rewrite according to review
haakonflatval-cognite Jul 12, 2024
ff01008
chore: lint fix
haakonflatval-cognite Jul 12, 2024
e086ad7
fix: logic related to removing/adding pending/created observations
haakonflatval-cognite Jul 15, 2024
7b85b26
chore: don't select interactive
haakonflatval-cognite Jul 15, 2024
d6e7034
chore: simplify pending-queries
haakonflatval-cognite Jul 15, 2024
b578650
feat: handle clipping
haakonflatval-cognite Jul 15, 2024
c82151e
chore: simplify overlay selection code
haakonflatval-cognite Jul 15, 2024
ee20745
chore: only save through SaveCommand
haakonflatval-cognite Jul 15, 2024
0655249
chore: small refactor, increase type-safety of intersection
haakonflatval-cognite Jul 15, 2024
6f6816b
chore: lint fix
haakonflatval-cognite Jul 15, 2024
fbf4dee
chore: revert unintended changes to util function
haakonflatval-cognite Jul 15, 2024
9bdc575
chore: don't store fdmSdk in cache
haakonflatval-cognite Jul 15, 2024
b71810f
chore: use more getters
haakonflatval-cognite Jul 16, 2024
67003be
chore: move public methods
haakonflatval-cognite Jul 16, 2024
60d4e8b
chore: simplify bounding box computation, invalidate stuff, skip clip…
haakonflatval-cognite Jul 16, 2024
0dff588
Merge branch 'master' into hflatval/observation-creation
haakonflatval-cognite Jul 16, 2024
40d357a
Merge branch 'master' into hflatval/observation-creation
haakonflatval-cognite Jul 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IconType } from '@cognite/cogs.js';
import { TranslateKey } from '../../base/utilities/TranslateKey';
import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand';
import { ButtonType } from '../../../components/Architecture/types';
import { ObservationsTool } from './ObservationsTool';
import { ObservationsCommand } from './ObservationsCommand';

export class CreateObservationCommand extends ObservationsCommand {
public override get icon(): IconType {
return 'Plus';
}

public override get tooltip(): TranslateKey {
return { key: 'ADD_OBSERVATION', fallback: 'Add observation. Click at a point' };
}

protected override invokeCore(): boolean {
const tool = this.getTool();
if (tool === undefined) {
return false;
}

tool.setIsCreating(!tool.isCreating);

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { IconType } from '@cognite/cogs.js';
import { TranslateKey } from '../../base/utilities/TranslateKey';
import { ButtonType } from '../../../components/Architecture/types';
import { ObservationsCommand } from './ObservationsCommand';

export class DeleteObservationCommand extends ObservationsCommand {
public override get icon(): IconType {
return 'Delete';
}

public override get tooltip(): TranslateKey {
return { fallback: 'Delete observation' };
}

public override get buttonType(): ButtonType {
return 'ghost-destructive';
}

public override get shortCutKey(): string {
return 'DELETE';
}

public override get isEnabled(): boolean {
const observation = this.getObservationsDomainObject();

return observation?.getSelectedOverlay() !== undefined;
}

protected override invokeCore(): boolean {
const observations = this.getObservationsDomainObject();
const selectedOverlay = observations?.getSelectedOverlay();
if (selectedOverlay === undefined) {
return false;
}

observations?.removeObservation(selectedOverlay);

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Color } from 'three';
import {
convertToSelectedColor,
DEFAULT_OVERLAY_COLOR,
PENDING_DELETION_OVERLAY_COLOR,
PENDING_OVERLAY_COLOR
} from './color';
import { assertNever } from '../../../utilities/assertNever';

export enum ObservationStatus {
Normal,
PendingCreation,
PendingDeletion
}

export function getColorFromStatus(status: ObservationStatus, selected: boolean): Color {
const baseColor = getBaseColor(status);

if (selected) {
return convertToSelectedColor(baseColor);
}

return baseColor;

function getBaseColor(status: ObservationStatus): Color {
switch (status) {
case ObservationStatus.Normal:
return DEFAULT_OVERLAY_COLOR;
case ObservationStatus.PendingCreation:
return PENDING_OVERLAY_COLOR;
case ObservationStatus.PendingDeletion:
return PENDING_DELETION_OVERLAY_COLOR;
default:
assertNever(status);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { CDF_TO_VIEWER_TRANSFORMATION, Overlay3D, Overlay3DCollection } from '@cognite/reveal';
import { FdmSDK } from '../../../utilities/FdmSDK';
import { Observation, ObservationProperties } from './models';
import { Vector3 } from 'three';

import { isPendingObservation, ObservationCollection, ObservationOverlay } from './types';
import {
createObservationInstances,
deleteObservationInstances,
fetchObservations
} from './network';
import { ObservationStatus } from './ObservationStatus';

/**
* A cache that takes care of loading the observations, but also buffers changes to the overlays
* list when e.g. adding or removing observations
*/
export class ObservationsCache {
private _loadedPromise: Promise<void>;
private _fdmSdk: FdmSDK;

private _persistedCollection = new Overlay3DCollection<Observation>([]);

private _pendingOverlaysCollection = new Overlay3DCollection<ObservationProperties>([]);

private _pendingDeletionObservations: Set<Overlay3D<Observation>> = new Set();

constructor(fdmSdk: FdmSDK) {
this._loadedPromise = fetchObservations(fdmSdk)
.then((data) => this.initializeCollection(data))
.then();
this._fdmSdk = fdmSdk;
}

public getFinishedOriginalLoadingPromise(): Promise<void> {
return this._loadedPromise;
}

public getPersistedCollection(): Overlay3DCollection<Observation> {
return this._persistedCollection;
}

public getPendingCollection(): Overlay3DCollection<ObservationProperties> {
return this._pendingOverlaysCollection;
}

public getOverlaysPendingForRemoval(): Set<Overlay3D<Observation>> {
return this._pendingDeletionObservations;
}

public getCollections(): ObservationCollection[] {
return [this._persistedCollection, this._pendingOverlaysCollection];
}

public addPendingObservation(
point: Vector3,
observation: ObservationProperties
): Overlay3D<ObservationProperties> {
return this._pendingOverlaysCollection.addOverlays([
{ position: point, content: observation }
])[0];
}

public removeObservation(observation: ObservationOverlay): void {
if (isPendingObservation(observation)) {
this.removePendingObservation(observation);
} else {
this.markObservationForRemoval(observation);
}
}

public async save(): Promise<void> {
await this._loadedPromise;
await this.savePendingObservations();
await this.removeDeletedObservations();
}

public removePendingObservation(observation: Overlay3D<ObservationProperties>): void {
this._pendingOverlaysCollection.removeOverlays([observation]);
}

public markObservationForRemoval(observation: Overlay3D<Observation>): void {
this._pendingDeletionObservations.add(observation);
}

public getObservationStatus(observation: ObservationOverlay): ObservationStatus {
if (isPendingObservation(observation)) {
return ObservationStatus.PendingCreation;
} else if (
!isPendingObservation(observation) &&
this._pendingDeletionObservations.has(observation)
) {
return ObservationStatus.PendingDeletion;
} else {
return ObservationStatus.Normal;
}
}

private async removeDeletedObservations(): Promise<void> {
const overlaysToRemove = [...this._pendingDeletionObservations];

await deleteObservationInstances(this._fdmSdk, overlaysToRemove);

this._persistedCollection.removeOverlays(overlaysToRemove);
this._pendingDeletionObservations.clear();
}

private async savePendingObservations(): Promise<void> {
const overlays = this._pendingOverlaysCollection.getOverlays();
if (overlays.length === 0) {
return;
}

const instances = await createObservationInstances(this._fdmSdk, overlays);

const overlayPositions = overlays.map((overlay) => overlay.getPosition());

this._pendingOverlaysCollection.removeAllOverlays();
this._persistedCollection.addOverlays(
instances.map((instance, index) => ({ content: instance, position: overlayPositions[index] }))
);
}

private initializeCollection(observations: Observation[]) {
const observationOverlays = observations.map((observation) => {
const position = new Vector3(
observation.properties.positionX,
observation.properties.positionY,
observation.properties.positionZ
).applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION);

return {
position,
content: observation
};
});

this._persistedCollection.addOverlays(observationOverlays);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand';
import { ObservationsDomainObject } from './ObservationsDomainObject';
import { ObservationsTool } from './ObservationsTool';

export abstract class ObservationsCommand extends RenderTargetCommand {
protected getTool(): ObservationsTool | undefined {
if (this.activeTool instanceof ObservationsTool) {
return this.activeTool;
}

return undefined;
}

protected getObservationsDomainObject(): ObservationsDomainObject | undefined {
return this.rootDomainObject.getDescendantByType(ObservationsDomainObject);
}
}
Loading
Loading